스레드 객체를 생성하고 start() 메소드를 호출했다고 해서 스레드가 바로 실행되는 것이 아니라 실행 대기 상태가 되는 것이다.
실행 대기 상태란, 언제든지 실행할 준비가 되어있는 상태를 의미한다.
운영체제는 실행 대기 상태인 스레드 중 하나를 선택하여 실행 상태로 만든다.
실행 상태인 스레드는 run() 메소드를 모두 수행하기 전에 다시 실행 대기 상태로 돌아갈 수 있으며 실행 대기 상태인 다른 스레드가 실행 상태로 변경되기도 한다.
실행 상태에서 run() 메소드를 모두 수행하면 스레드 실행이 멈추고 종료 상태가 된다.
- 실행 대기:
start()메소드 호출- 실행:
run()메소드 호출- 일시정지: 스레드를 실행할 수 없는 상태, 일시정지 -> 실행 X / 일시정지 -> 실행 대기 -> 실행
- 종료:
run()메소드 종료
스레드는 [실행 대기, 실행] 상태를 번갈아가며 run() 메소드를 조금씩 수행한다.
그러다가 run() 메소드가 종료되면 종료 상태로 전환된다.
스레드를 실행할 수 없는 상태에 빠지게 되면 일시정지 상태로 전환되며, 이 때 바로 실행 상태로 전환이 불가능하고 실행 대기 상태를 거쳐 실행 상태로 전환할 수 있다.
스레드 상태 제어 메소드
- Interrupt(): 일시 정지 상태의 스레드에서
InterruptedException을 발생시켜, 예외 처리 코드에서 실행 대기 상태로 가거나 종료 상태로 갈 수 있도록 한다.- sleep(long millis): 주어진 시간 동안 스레드를 일시 정지 상태로 만든다. 주어진 시간이 지나면 자동으로 실행된다.
- stop(): 스레드를 즉시 종료한다. 불안정한 종료를 유발하므로 사용을 지양한다.
실행 중인 스레드를 즉시 종료해야하는 경우 stop() 메소드를 사용하게 된다.
이 경우 스레드가 사용 중이던 자원(파일, 네트워크 연결 등)이 불안전한 상태로 남겨지기 때문에 이제는 사용되지 않는다.
stop 플래그를 이용하여 run() 메소드의 종료를 유도하는 방식이다.
public class TestThread extends Thread{
private boolean stop;
public void setStop(boolean stop){
this.stop = stop;
}
@Override
public void run(){
while(!stop) {
System.out.println("스레드 실행 중");
}
System.out.println("스레드 자원 정리");
System.out.println("스레드 실행 종료");
}
}
TestThread stopThread = new TestThread();
stopThread.start();
try {
Thread.sleep(300);
}catch (InterruptedException e){
System.out.println("InterruptedException");
}
stopThread.setStop(true);
출력 결과
스레드 실행 중
...
스레드 실행 중
스레드 자원 정리
스레드 실행 종료
메인 스레드가 0.3초 일시 정지하며 그 동안 TestThread 스레드는 열심히 while문을 수행한다.
0.3초가 지나면 메인 스레드가 재실행되며 stopThread.setStop(true); 코드가 실행되면서 while문을 탈출하고 run() 메소드의 마지막 코드까지 수행 후 메소드를 정상 종료한다.
interrupt() 메소드는 스레드가 일시정지 상태 있을 때 InturruptedException을 발생시킨다.
이 부분을 이용해 run() 메소드를 정상 종료할 수 있다.
public class TestThread extends Thread {
@Override
public void run() {
try {
while (true) {
System.out.println("스레드 실행 중");
Thread.sleep(10);
}
}catch (InterruptedException e){
System.out.println("InterruptedException");
}
System.out.println("스레드 자원 정리");
System.out.println("스레드 실행 종료");
}
}
TestThread stopThread = new TestThread();
stopThread.start();
try {
Thread.sleep(100);
}catch (InterruptedException e){
}
stopThread.interrupt();
출력 결과
스레드 실행 중
...
스레드 실행 중
InterruptedException
스레드 자원 정리
스레드 실행 종료
일시정지 상태와 interrupt() 메소드를 활용한 Exception 발생을 통한 while문 탈출 기법이다.
하지만 이 방법을 사용하려면 메소드가 잠시라도 일시정지 상태가 되어야한다는 조건이 따른다.
만약 TestThread의 Thread.sleep(10); 코드가 없었다면 interrupt() 메소드는 의미가 없는 것이다.
Thread.sleep()을 사용하지 않고 inturrput() 메소드를 이용하여 while문 탈출하는 방법으로 Thread.interrupted() 메소드를 이용하는 방법이 있다.
이 메소드는 해당 객체의 interrupt()가 호출되었는 지 확인하는 메소드이다.
public class TestThread extends Thread {
@Override
public void run() {
while (true) {
System.out.println("스레드 실행 중");
if(Thread.interrupted()){
break;
}
}
System.out.println("스레드 자원 정리");
System.out.println("스레드 실행 종료");
}
}
출력 결과
스레드 실행 중
...
스레드 실행 중
스레드 자원 정리
스레드 실행 종료
데몬 스레드란 주 스레드의 보조적인 역할을 수행하는 스레드이다. 따라서 주 스레드가 종료되면 데몬 스레드는 강제 종료된다.
public class TestThread extends Thread {
public void safe(){
System.out.println("작업 저장");
}
@Override
public void run() {
System.out.println("보조 스레드 실행");
while (true) {
try{
Thread.sleep(1000);
}catch (InterruptedException e){
System.out.println("InterruptedException");
break;
}catch (Exception e){
System.out.println("Exception");
break;
}
safe();
}
System.out.println("보조 스레드 자원 정리");
System.out.println("보조 스레드 실행 종료");
}
}
주 스레드가 될 객체가 데몬이 될 스레드의 setDaemon(true)를 호출해주면 생성된다.
해당 객체가 데몬 스레드인지 확인하고자 한다면 Thread.isDaemon()을 호출하면된다.
결과 값으로 boolean값을 리턴한다.
TestThread daemonThread = new TestThread();
daemonThread.setDaemon(true);
System.out.println(daemonThread.isDaemon()); //true
주의할 점!
start() 메소드가 실행된 후에 setDaemon()을 호출하면 IllegalThreadStateException이 발생한다.
때문에 데몬 스레드를 설정하고자 한다면 꼭 start() 메소드 실행 전에 호출하여야한다.
궁금해서 예외 발생하는 코드 작성해보기..
TestThread daemonThread = new TestThread();
try {
daemonThread.start();
daemonThread.setDaemon(true);
Thread.sleep(3000);
}catch (InterruptedException e){
System.out.println("InterruptedException");
}catch (IllegalThreadStateException e){
System.out.println("IllegalThreadStateException");
}
System.out.println("메인 스레드 종료");
출력 결과
IllegalThreadStateException
메인 스레드 종료
보조 스레드 실행
작업 저장
...
예외처리도 수행됐고 메인 스레드도 종료됐지만 TestThread가 데몬 스레드로 세팅되지 않은 상태로 시작되어 데몬 스레드로 생성한 객체가 그냥 계속 동작한다.
// 수정 전
daemonThread.start();
daemonThread.setDaemon(true);
// 수정 후
if(daemonThread.isDaemon()) daemonThread.setDaemon(ture);
daemonThread.start();
이렇게 수정하니 의도한대로 동작한다!