본문 바로가기

Java

Java 스레드(thread)

 

 

1. 프로세스(Process)

실행 중인 프로그램, 자원(Resources)과 스레드로 구성

 

2. 스레드(Thread)

- 프로세스 내에서 실제 작업을 수행하는 것, 모든 프로세시는 하나의 스레드를 갖음

- 경량 프로세스(LWP, Light-Weight Process)라고 부르기도 함

 

3. 스레드의 구현

1) Thread 클래스 상속 받는 방법

// Thread 클래스를 상속
class MyThread extends Thread {
    public void run() { // Thread클래스의 run()메서드 오버라이딩
    	// 작업 내용
    }
}

// 실행
MyThread t1 = new MyThread(); // 스레드 생성
t1.start(); // 스레드의 실행

 

 

2) Runnable 인터페이스 구현하는 방법

👉🏻 다른 클래스를 상속받기 위해 인터페이스를 구현하는 방법이 일반적

👉🏻 재사용성(reusability)이 높고, 코드의 일관성(consistency)을 유지할 수 있는 객체지향적인 방법

// Runnable 인터페이스 구현
class MyThread implemnets Runnable {
    public void run() { // Runnable 인터페이스의 추상메서드 run() 구현
    	// 작업내용
    }
}

// 실행
Runnable r = new MyThread(); // 스레드 생성
Thread t = new Thread(r); // Thread(Runnable r)
t.start(); // 스레드의 실행

 

 

4. 스레드의 실행 start(), run() 

- 스레드를 생성한 후 호출해야 스레드가 작업을 시작함

ThreadTest1 t1 = new ThreadTest1(); // 스레드 t1 생성
ThreadTest1 t2 = new ThreadTest1(); // 스레드 t2 생성

t1.start(); // start()메서드는 호출 스택을 생성하고, run()메서드를 호출하여 실행
t2.start();

- OS 스케줄러가 실행 순서를 결정함(생성한다고 실행되는 것이 아니고, 순서도 OS 스케줄러가 결정) 

- start()메서드: 새로운 스레드를 생성하고 스레드가 작업하는데 사용될 호출 스택 생성

- run()메서드: 생성된 호출 스택에서 작업 실행 

 

5. Main 스레드

- 프로그램을 실행하면 기본적으로 하나의 스레드를 생성하고 그 스레드가 main메서드를 호출해서 작업이 수행 됨

 

 

6. 싱글 스레드와 멀티 스레드

1) 싱글 스레드  

- 싱글 스레드 프로세스: 자원 + 스레드, 작업을 하나의 스레드로 처리하는 것

 

2) 멀티 스레드

- 멀티 스레드 프로세스: 자원 + 스레드 + 스레드 + 스레드 +..., 작업을 여러개의 스레드를 번갈아가며 처리하는 것

- 멀티 스레드의 장단점

멀티 스레드 장점 멀티 스레드 단점
- CPU 사용률 향상 
- 자원의 효율적 사용
- 사용자에 대한 응답성(responseness) 향상
- 작업이 분리되어 코드가 간결해짐
- 동기화(Synchronization)에 주의 필요
- 교착상태(deadlock)가 발생하지 않도록 주의 필요
- 각 스레드가 효율적으로 고르게 실행될 수 있도록 해야함

 

3) 싱글 스레드와 멀티 스레드

- 문맥 전환(context switching): 멀티 스레드 또는 멀티 프로세스로 작업 하는 경우, 스레드와 프로세스를 번갈아가면서 작업하는데 스레드나 프로세스 간 작업 전환을 '컨텍스트 스위칭'이라고 함

- 컨텍스트 스위칭 시 작업상태 등을 저장하고 읽는 시간이 추가적으로 필요함

- 싱글 코어에서 단순히 CPU만 사용하는 작업이라면 오히려 싱글 스레드로 프로그래밍하는 것이 효율적일 수 있음

- 싱글 코어에서 한 스레드가 입출력 작업을 처리하는 동안 다른 스레드는 대기를 해야하는 경우가 발생할 수 있는데 그 대기 시간때문에 시간이 더 걸릴 수 있음(멀티 코어인 경우에도 싱글 코어보단 대기시간이 줄 수는 있지만 대기 시간이 발생하긴 함)

 

 

7. 스레드의 I/O블락킹(bolocking)

- 스레드가 입출력(I/O) 처리를 위해 기다리는 것을 I/O블락킹이라고 함 

- 멀티 스레드로 처리 하면 입출력 처리를 위해 기다리는 시간 동안 다른 스레드가 작업을 처리할 수 있기 때문에 효율적

 

 

8. 스레드의 우선순위(priority)

- 스레드는 우선순위라는 속성(멤버변수)을 가지고 있음

- 우선순위의 값에 따라 스레드가 얻는 실행시간이 달라짐

- 우선순위의 범위는 1~10(숫자가 높을수록 우선순위가 높음)

- 우선순위는 스레드를 생성한 스레드로부터 상속 받음

- 스레드의 우선순위 관련 메서드와 상수

void setPriority(int newPriority) // 스레드의 우선순위를 지정한 값으로 변경
in getPriority() // 스레드의 우선순위 반환

public static final int MAX_PRIORITY = 10 // 최대 우선순위
public static final int MIN_PRIORITY = 1 // 최소 우선순위
public static final int NORM_PRIORITY = 5 // 보통 우선순위

- 우선순위는 지정하지만 희망사항 같은 것..! 우선순위를 지정하긴 하지만 그대로 우선순위가 지켜지진 않음.. OS스케줄러가 결정하는 것

👉🏻 OS 스케줄러의 종속적

 

 

9. 스레드 그룹(thread group)

- 서로 관련된 스레드를 그룹으로 다루기 위한 것

- 모든 스레드는 반드시 하나의 스레드 그룹에 포함 되어야함

👉🏻 자바 어플리케이션 실행 시 스레드 그룹을 지정하지 않는 경우 main 스레드 그룹에 속하게 됨

- 스레드 그룹에 다른 스레드 그룹을 포함시킬 수 있음

- 보안상의 이유로 도입된 개념으로, 자심이 속한 스레드 그룹이나 하위 스레드 그룹은 변경 가능하나 다른 스레드 그룹의 스레드 변경 불가

 

 

1) 스레드를 스레드 그룹에 포함시키는 방법

- Thread의 생성자 이용

Thread(ThreadGroup group, String name)
Thread(ThreadGroup group, Runnable target)
Thread(ThreadGroup group, Runnable target, String name)
Thread(ThreadGroup group, Runnable target, String name, long stackSize)

 

2) 스레드 그룹과 관련된 메서드

ThreadGroup getThreadGroup() // 자신이 속한 스레드 그룹 반환 
void uncaughtException(Thread t, Throwable e) // 처리되지 않은 예외로 인해 스레드 그룹의 스레드 실행이 종료된 경우, JVM에 의해 이 메서드 자동 호출 됨

 

https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/Thread.html

 

Thread (Java SE 11 & JDK 11 )

Allocates a new Thread object so that it has target as its run object, has the specified name as its name, belongs to the thread group referred to by group, has the specified stackSize, and inherits initial values for inheritable thread-local variables if

docs.oracle.com

 

 

10. 스레드의 종류

1) 일반 스레드(=사용자 스레드)

- 데몬 스레드가 아닌 스레드

 

2) 데몬 스레드

- 일반 스레드의 작업을 돕는 보조적 역할을 수행하는 스레드

- 일반 스레드가 종료되면 데몬 스레드는 강제 종료 됨(데몬 스레드의 존재의 의미가 사라지기 때문)

- 데몬 스레드의 예: 가비지 컬렉터, 워드프로세서의 자동저장, 화면 자동 갱신 등

 

① 데몬 스레드의 작성과 실행

- 일반 스레드의 작성 방법과 실행 방법이 같음, 다만 스레드 생성과 실행 사이에 setDaemon(true)를 호출함

 

② 데몬 스레드 관련 메서드

boolean isDaemon() //  스레드가 데몬 스레드인지 확인, 맞으면 true 반환
void setDaemon(boolean on) // 스레드를 데몬 스레드 또는 사용자 스레드로 변경, 매개변수 on의 값을 true로 지정하면 데몬 스레드가 됨

 

 

11. 스레드의 상태

1) 생성(NEW)

- 스레드가 생성되고, start()가 호출되지 않은 상태

 

2) 실행대기(RUNNABLE)

- 실행 가능한 상태

- 실행 대기열은 큐(queue)와 같은 구조로 먼저 실행 대기열에 들어온 스레드가 먼저 실행됨

 

3) 실행

- 실행 상태

 

4) 일시정지

- BLOCKED: 동기화 블럭에 의해서 일시정지된 상태(LOCK이 풀릴 때 까지 기다리는 상태)

- WAITING: 일시 정지 상태

 

5) 종료(TERMINATED)

- 스레드 작업 종료 상태

 

 

12. 스레드의 실행제어

- 스케줄링 관련 메서드

메서드 설명
static void sleep(long millis)
static void sleep(long millis, int nanos)
- 지정된 시간동안 스레드 일시정지 , 지정된 시간 후 자동으로 실행 대기
- 지정 시간 범위: 밀리세컨드(1000분의 일초), 나노세컨드( 10억분의 일초)
void join()
void join(long millis)
void join(long millis, int nanos)
지정된 시간동안 스레드 실행, 지정된 시간이 지나거나 작업 종료 시 join()을 호출한 스레드로 돌아감
void interrupt() - sleep(), join() 메서드에 의해 일시정지된 스레드를 실행 대기 상태로 만듦
- 해당 스레드에서는 Interrupted Exception이 발생함으로써 일시정지 상태를 벗어나게 됨
void stop() - 스레드를 즉시 종료 시킴
void suspend() - 스레드를 일시 정지 시킴
- resume() 호출 시 다시 실행 대기 상태기 됨
void resume() - suspend()에 의해 일시 정지 상태인 스레드를 실행 대기 상태로 만듦
static void yiedl() - 실행중에 자신에게 주어진 실행시간을 다른 스레드에게 양보하고 자신은 실행 대기 상태가 됨

* stop(), suspend(), resume()은 스레드를 교착상태(deadlock)로 만들기 쉽기 때문에 deprecated 됨

 

13. 스레드의 동기화(synchronization) 

- 한 스레드가 진행중인 작업을 다른 스레드가 간섭하지 못하게 막는 것

- 공유 데이터를 사용하는 코드 영역을 임계 영역(critical section)으로 지정하고, 공유 데이터(객체)가 갖고 있는 잠금(lock)을 획득한 단 하나의 스레드만 이 영역의 코드를 수행 할 수 있음

- 모든 객체는 lock을 하나씩 가지고 있으며, 다른 스레드들은 lock을 얻을 때 까지 대기상태가 되므로 임계 영역을 최소화해야 프로그램 성능에 좋음 

 

1) 동기화 방법(임계 영역 설정)

① 메서드 전체를 임계 영역으로 설정

public sychronized void calSum() { // 메서드 앞에 synchronized를 붙임
// ...
}

 

② 특정 영역을 임계 영역으로 설정

sychronized(객체의 참조변수) { // 메서드 내의 코드 일부를 블럭'{}'으로 감싸고 블럭앞에 synchronized를 붙임
// ...
}

 

 

2) 동기화 관련 메서드

- Object에 정의 되어 있음

- 동기화 블록 내에서만 사용 가능

- 보다 효율적인 동기화를 가능하게 함

메서드 설명
wait() - 실행 중인 스레드를 일시 정지 상태로 만듦
- notify(), notifyAll()이 호출 될 때 까지 기다림
- 매개변수가 있는 wait()은 지정된 시간동안만 대기
notify() - waiting pool에 있던 스레드를 중 임의의 스레드에게 통지하여 실행 대기 상태로 만듦
- 매개변수가 있는 wait()는 지정된 시간이 지난 후 자동적으로 notify()를 호출하여 실행 대기 상태로 만듦
notifyAll() - 호출된 객체의 waiting pool에 대기중인 스레드를 실행 대기 상태로 만듦

 

 

'Java' 카테고리의 다른 글

Java 스트림(stream)  (0) 2023.01.20
Java 람다(Lambda)  (0) 2023.01.17
Java 애너테이션(annotation)이란?  (0) 2023.01.17
Java 열거형(enum)  (0) 2023.01.17
[Java] 제네릭(Generics)  (0) 2023.01.10