12-2 / 스레드 제어

스레드 객체 생성하고 start() 메소드를 호출하면 실행 대기 상태가 된다.

운영체제는 실행 대기 상태에 있는 스레드 중 하나를 선택해서 실행 상태로 만든다.

 

실행 상태의 스레드는 run() 메소드를 모두 실행하기 "전"에 다시 실행 대기 상태로 돌아갈 수 있다.

실행 대기 상태에 있는 다른 스레드가 선택되어 실행 상태가 되기도 한다.

 

실행 상태에서 run() 메소드 내용이 모두 실행되면 스레드의 실행이 멈추고 종료 상태가 된다.

 


스레드 상태

  • 실행 대기 상태 : 스레드는 start() 메소드를 호출하면 실행을 기다리고 있는 실행 대기 상태가 된다. 
  • 실행 상태 (running) : 실행 대기 상태에 있는 스레드 중 운영체제가 하나의 스레드를 선택하고 CPU가 run()메소드를 실행한 상태
  • 대기 상태와 실행 상태의 반복 : 실행 상태의 스레드는 run()메소드가 모두 실행하기 전 다시 실행 상태로 돌아갈 수 있고, 실행 대기 상태의 다른 스레드가 선택되어 실행 상태가 되기도 한다. 이런 과정을 반복하여 스레드는 자신의 run() 메소드를 조금씩 실행한다. 
  • 일시정지 : 경우 따라 일시정지 상태로 가기도 하며, 일시정지 상태가 풀리면 바로 실행이 아닌 실행 대기 상태로 돌아가 실행을 기다리게 된다.

  • 종료 상태 (terminated) : 위과정을 반복하다가 더이상 실행할 코드가 없게 되면 run()메소드가 종료되고 스레드 실행은 멈추어 종료 상태가 된다.

 


스레드 상태 제어

동영상을 재생하다 일시정지를 하는 등 실행되고 있는 스레드의 행동을 하려면 스레드의 상태를 변경해야 하는데 이를 스레드 상태 제어라고 한다. 특히 멀티 스레드 프로그램에서는 더더욱 정교한 스레드 상태 제어가 필요하다.

 

메소드 설명
interrupt() 일시 정지 상태의 스레드에서 InterruptedException을 발생시켜, 예외 처리코드(catch)에서 실행 대기 상태로 가거나 종료 상태로 갈 수 있게 한다.
sleep(long millis) 주어진 시간 동안 스레드를 일시정지. 시간이 지나면 자동적으로 실행 대기 상태가 된다.
stop() 스레드 즉시 종료. (불안전한 종료가 되므로 안정성에 문제가 있어 Deprecated 되었다.)

 

  • 주어진 시간 동안 일시정지
try {
	Thread.sleep(1000);
} catch (InterruptedException e) {
	// interrupt() 메소드가 호출되면 실행
}

위의 코드는 1000 밀리세컨드  즉 1초만 스레드를 일시정지 상태로 만든다. 그러나 1초 안에 interrupt() 메소드가 호출되면 InterruptedException이 발생하게 되고 catch문이 이걸 잡아 해당 실행 블록을 실행한다.

 

  • 스레드의 안전한 종료

스레드는 자신의 run() 메소드가 모두 실행되면 자동 종료된다. 하지만 경우에 따라 중간에 즉시 종료해야할 경우가 있는데 stop() 메소드는 종료시 사용하던 자원들을 불안전한 상태로 남겨 안정성 문제로 deprecated(중요도가 떨어져 이제 사용X) 되었다. 그렇다면 안전하게 종료할 수 있는 방법은 무엇이 있을까

 

① stop 플래그 이용하여 run()메소드의 종료를 유도한다.

public class XXXThread extends Thread {
    private boolean stop; 	// stop 플래그 필드
    
    public void run(){
		while( !stop ) { 	// stop이 true가 되면 run() 종료
        	스레드가 반복 실행하는 코드
        }
    }
}

stop이 true가 되면 run을 종료하고 스레드가 사용한 자원을 정리하며 run()메소드가 끝나 안전하게 종료한다.

 

 

② interrupt() 메소드 이용하는 방법

public class InterruptExample {
    public static void main(String[] args){
        Thread thread = new PrintThread2();
        thread.start();
        
        try { 
        	Thread.sleep(1000); 
        } catch (InterruptedException e){ }
        
        thread.interrupt(); // 스레드 종료를 위해 InterruptedException 발생
    }
}
public class PrintTread2 extends Thread {
    public void run(){
    	try {
            while(true){
            	System.out.println("실행 중");
                Thread.sleep(1);	// InterruptedException 발생
            }
        } catch (InterruptedException e) { }
        
        System.out.println("자원 정리");
        System.out.println("실행 종료");
    }
}

실행 대기 또는 실행 상태에서 interrupt() 메소드가 실행되면 즉시 InterruptedException가 발생하지 않고 "미래에" 일시 정지 상태가 되면 InterruptedException 발생한다. 따라서 interrupt()메소드는 일시정지 상태가 아니면 메소드 호출의 의미가 없다. 그래서 강제로 나마 짧은 일시 정지를 만들어 InterruptedException 발생시키기 위해 중간에 2번째 코드에 Thread.sleep(1); 이 들어간 것이다.

 

일시 정지를 하지 않고 interrupt의 호출 여부를 알려면 interrupted() 메소드와 isInterrupted() 메소드를 사용하면 되며 interrupt() 호출시 이 두 메소드는 true를 리턴한다. 

- interrupted()  :  정적 메소드로 현재 스레드가 interrupted 되었는지 확인

- isInterrupted() :  인스턴스 메소드로 현재 스레드가 interrupted 되었는지 확인

 

이 함수를 이용해 if문의 조건으로 사용해 while문을 빠져나오게 하여 스레드가 자원을 정리하게 유도한다.

 

 


데몬 스레드 

스레드의 작업을 돕는 보조적인 역할을 수행하는 스레드. 주 스레드의 보조 역할을 수행하기 때문에 주 스레드가 종료되면 데몬 스레드는 강제적으로 자동 종료된다. 이 점을 제외하면 데몬 스레드는 일반 스레드와 큰 차이가 없다.

 

스레드를 데몬으로 만들기 위해 주 스레드가 데몬이 될 스레드의 setDaemeon(true)를 호출해주면 된다. 

 

주의할 점은 start()메소드가 호출되고 나서 setDaemeon(true)을 호출하면 IllegalThreadStateException이 발생하기 때문에 start() 메소드 호출전에 setDaemeon(true)를 호출해야 한다.

+ Recent posts