스레드 제어와 생명 주기
스레드 기본 정보
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class ThreadInfoMain {
public static void main(String[] args) {
Thread mainThread = Thread.currentThread();
log("mainThread = " + mainThread);
log("mainThread.threadId() = " + mainThread.threadId());
log("mainThread.getName() = " + mainThread.getName());
log("mainThread.getPriority() = " + mainThread.getPriority());
log("mainThread.getThreadGroup() = " + mainThread.getThreadGroup());
log("mainThread.getState() = " + mainThread.getState());
Thread myThread = new Thread(new HelloRunnable(), "myThread");
log("mainThread = " + myThread);
log("mainThread.threadId() = " + myThread.threadId());
log("mainThread.getName() = " + myThread.getName());
log("mainThread.getPriority() = " + myThread.getPriority());
log("mainThread.getThreadGroup() = " + myThread.getThreadGroup());
log("mainThread.getState() = " + myThread.getState());
}
}
1
2
3
4
5
6
7
8
9
10
11
12
21:07:01.745 [ main] mainThread = Thread[#1,main,5,main] // 스레드 정보 출력
21:07:01.752 [ main] mainThread.threadId() = 1 // 스레드 아이디
21:07:01.752 [ main] mainThread.getName() = main // 스레드 이름
21:07:01.755 [ main] mainThread.getPriority() = 5 // 스레드 우선순위
21:07:01.755 [ main] mainThread.getThreadGroup() = java.lang.ThreadGroup[name=main,maxpri=10] // 스레드 그룹
21:07:01.755 [ main] mainThread.getState() = RUNNABLE // 스레드 상태
21:07:01.756 [ main] mainThread = Thread[#21,myThread,5,main]
21:07:01.756 [ main] mainThread.threadId() = 21
21:07:01.756 [ main] mainThread.getName() = myThread
21:07:01.757 [ main] mainThread.getPriority() = 5
21:07:01.757 [ main] mainThread.getThreadGroup() = java.lang.ThreadGroup[name=main,maxpri=10]
21:07:01.758 [ main] mainThread.getState() = NEW
스레드 출력
thread.toString()
: 스레드 ID, 스레드 이름, 우선순위, 스레드 그룹을 반환한다.
스레드 아이디
thread id
: 자바에서 자동 생성하여 부여한다. 또한 JVM 내에서 다른 스레드 아이디와 중복되지 않는다.
- 스레드 아이디는 직접 설정하는 것이 불가능하다.
스레드 이름
name
스레드 이름. 설정하지 않으면 자동으로 부여되며, 이름은 중복될 수 있다.
스레드 우선순위
priority
(default : 5) : 우선순위
setPriority()
메서드를 사용하여 1(가장 낮음)부터 10(가장 높음)까지 설정할 수 있으며 우선순위가 높을 수록 더 많이 실행된다.- 운영체제 스케줄링에 힌트를 주지만 우선순위가 높다고 항상 더 많이 실행되는 것은 아님. (운영체제마다 상이함)
- 보통 운영체제에서 알아서 조정하기 때문에, 자바 백엔드 개발할 때 직접 조정하는 일은 거의 없음.
스레드 그룹
thread group
: 스레드가 속한 그룹
- 스레드 그룹은 스레드를 그룹화하여 관리할 수 있는 기능을 제공한다.
- 기본적으로 모든 스레드는 부모 스레드와 동일한 스레드 그룹에 속하며, 특정 작업(예: 일괄 종료, 우선순위 설정 등)을 그룹단위로 수행할 수 있다.
- 부모 스레드(Parent Thread): 새로운 스레드를 생성하는 스레드를 의미한다.
스레드는 기본적으로 다른 스레드에 의해 생성된다. 이러한 생성 관계에서 새로 생성된 스레드는 생성한 스레드를 부모로 간주한다.- 예를 들어
myThread
는main
스레드에 의해 생성되었으므로main
스레드가 부모 스레드!
- 예를 들어
main
스레드는 기본으로 제공되는main
스레드 그룹에 소속되어 있다. 따라서myThread
도 부모 스레드인main
스레드의 그룹인main
스레드 그룹에 소속된다.
스레드의 현재 상태
state
: 스레드의 현재 상태
NEW
: 스레드가 아직 시작되지 않은 상태이다.RUNNABLE
: 스레드가 실행 중이거나 실행될 준비가 된 상태이다.BLOCKED
: 스레드가 동기화 락을 기다리는 상태이다.WAITING
: 스레드가 다른 스레드의 특정 작업이 완료되기를 기다리는 상태이다.TIMED_WAITING
: 일정 시간 동안 기다리는 상태이다.TERMINATED
: 스레드가 실행을 마친 상태이다.
스레드의 생명 주기
자바에서 스레드의 일시 중지 상태(Suspended State)라는 상태는 없다. 스레드가 기다리는 상태들을 묶어서 쉽게 설명하기 위해 사용한 용어임!
NEW 새로운 상태 : 생성되었으나, 시작되지 않은 상태 | |
Runnable 실행 가능한 상태 : 실행 중이거나 실행될 준비가 된 상태 | |
일시 중지 상태 (Suspended State) | Blocked 차단 상태 : 동기화 락을 기다리는 상태 |
Waiting 대기 상태 : 다른 스레드의 작업을 무기한으로 기다리는 상태 | |
Time Waiting 시간 제한 대기 상태: 일정 시간 동안 다른 스레드의 작업을 기다리는 상태 | |
Terminated 종료 상태: 실행이 완료된 상태 |
New 새로운 상태
생성되고 아직 시작되지 않은 상태
- 이 상태에서는
Thread
객체가 생성되지만,start()
메서드가 호출되지 않은 상태
Runnable 실행 가능 상태 (실행 상태)
스레드가 실행될 준비가 된 상태. 이 상태에서 스레드는 실제로 CPU에서 실행될 수 있다.
start()
메서드가 호출되었을 때
Runnable 상태에 있는 스레드가 모두 동시에 실행되는 것이 아니다. 스케줄러의 실행 대기열에 포함되어 있다가 차례대로 CPU에서 실행한다.
- 운영체제의 스케줄러의 실행 대기열에 있는 상태와 현재 실행되고 있는 상태 모두
RUNNABLE
상태이다.- 자바에서 이 두 상태를 구분할 수는 없다.
Blocked 차단 상태
스레드가 다른 스레드에 의해 동기화 락을 얻기 위해 기다리는 상태이다.
- 예를 들어,
synchronized
블록에 진입하기 위해 락을 얻어야 하는 경우 이 상태에 들어간다.
Waiting 대기 상태
스레드가 다른 스레드의 특정 작업이 완료되기를 무기한 기다리는 상태이다.
wait()
,join()
메서드가 호출될 때
스레드는 다른 스레드가 notify()
또는 notifyAll()
메서드를 호출하거나, join()
이 완료될 때까지 기다린다.
Timed Waiting (시간 제한 대기 상태)
스레드가 특정 시간 동안 다른 스레드의 작업이 완료되기를 기다리는 상태이다.
sleep(long millis)
,wait(long timeout)
,join(long millis)
메서드가 호출될 때
주어진 시간이 경과하거나 다른 스레드가 해당 스레드를 깨우면 이 상태에서 벗어난다.
Terminated (종료 상태)
스레드의 실행이 완료된 상태이다.
- 스레드가 정상적으로 종료되었거나 예외가 발생하여 종료된 경우
- 스레드는 한 번 종료되면 다시 시작할 수 없다.
코드
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
package thread.control;
import static util.MyLogger.log;
public class ThreadStateMain {
public static void main(String[] args) throws InterruptedException {
Thread myThread = new Thread(new MyRunnable(), "myThread");
log("myThread.state1 = " + myThread.getState()); // NEW
log("myThread.start()");
myThread.start();
Thread.sleep(1000);
// myThread TIMED_WAITING 상태 -> 자신의 상태 출력 불가능
// main 스레드에서 상태를 출력
log("myThread.state3 = " + myThread.getState()); // TIMED_WAITING
Thread.sleep(4000);
log("myThread.state5 = " + myThread.getState()); // TERMINATED
log("end");
}
static class MyRunnable implements Runnable {
@Override
public void run() {
try {
log("start");
log("myThread.state2 = " + Thread.currentThread().getState()); // RUNNABLE
log("sleep() start");
Thread.sleep(3000);
log("sleep() end");
log("myThread.state4 = " + Thread.currentThread().getState()); // RUNNABLE
log("end");
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
}
소스코드
1
2
3
4
5
6
7
8
9
10
11
22:56:54.248 [ main] myThread.state1 = NEW
22:56:54.251 [ main] myThread.start()
22:56:54.251 [ myThread] start
22:56:54.252 [ myThread] myThread.state2 = RUNNABLE
22:56:54.252 [ myThread] sleep() start
22:56:55.257 [ main] myThread.state3 = TIMED_WAITING
22:56:57.257 [ myThread] sleep() end
22:56:57.259 [ myThread] myThread.state4 = RUNNABLE
22:56:57.259 [ myThread] end
22:56:59.260 [ main] myThread.state5 = TERMINATED
22:56:59.261 [ main] end
실행 결과
1
22:56:55.257 [ main] myThread.state3 = TIMED_WAITING
위와 같이 main에서 myThread의 상태를 찍은 이유는 무엇일까?
- myThread는
sleep()
때문에TIMED_WAITING
상태이다.
따라서 다음과 같이 myThread에서 sleep() 이후 상태를 출력하고자 하면,TIMED_WAITING
상태에선 대기 상태이기 때문에 출력이 불가능하고, 출력 결과는 이후에 대기 상태가 끝난 뒤RUNNABLE
상태에서 상태를 출력하므로, 정확한 상태 출력이 불가능하다.1 2 3 4
log("sleep() start"); Thread.sleep(3000); log("myThread.state3 = " + Thread.currentThread().getState()); // RUNNABLE 이 출력됨. log("sleep() end");
체크 예외 재정의
1
2
3
public interface Runnable {
void run();
}
join
이 포스트는 “김영한의 실전 자바 - 고급 1편, 멀티스레드와 동시성” 강의를 듣고 작성하였습니다!
실습에 사용된 모든 코드는 이곳에서 확인할 수 있습니다.