Thread 상태를 조절하는 메서드

2 분 소요

Thread를 대기시키는 메서드

1. sleep()

명시된 시간만큼 Thread를 대기시킨다.

  • sleep(long mills) : ms 만큼 대기
  • sleep(long mills, int nanos) : ms + nano초 만큼 대기

2. wait()

Object에 선언되어 있다.
대기 시간 설정은 sleep() 와 동일하다. 시간 설정이 되어 있지 않을 경우 notify()나 notifyAll()이 호출될때까지 대기한다.

3. join()

파라미터로 입력받은 시간만큼 해당 Thread가 죽기를 기다린다.
대기 시간 설정은 sleep()와 동일하다. 시간 설정이 되어 있지 않을 경우 죽을때까지 기다린다.

A Thread가 B Thread의 join()을 호출하면 A Thread는 B Thread가 죽기 전까지 먼저 종료되지 않는다.

만약 시간을 설정하면 그 시간만큼만 B Thread의 종료를 기다리고 그때까지 B가 죽지 않으면 A는 그대로 종료한다.

죽는 것을 기다린다. 라는 표현이 이상하긴 하지만 레퍼런스에도 그렇게 되어 있다.

3.1. join의 사용 예

Main Thread가 있고, Work Thread 여러개가 있다. Main Thread는 Work Thread들이 끝날때까지 종료가 되면 안된다.
이 때 사용하는 것이 join() 이다.

public static void main(String[] args) {
    ArrayList<Thread> threads = new ArrayList<Thread>();
    for(int i=0; i<10; i++) {
        Thread t = new Test(i);
        t.start();
        threads.add(t);
    }

    for(int i=0; i<threads.size(); i++) {
        Thread t = threads.get(i);
        try {
            t.join();
        }catch(Exception e) { }
    }
    System.out.println("main end.");
}

Thread를 깨우는 메서드

1. interrupt()

Thread를 대기시키는 메서드 (sleep, wait, join)을 모두 깨울 수 있는 메서드이다.

interrupt()가 호출되면 해당 Thread에서 isInterrupted()의 결과가 true로 리턴되고, 때대로 InterruptedException이 발생하기때문에 인터럽트 여부를 확인할 수 있다.

1.1. InterruptedException 사용 주의 점

보통 interrupt()는 Thread를 중간에 종료시키고 싶을떄 사용한다.

그리고 interrupt()를 호출 했을때 발생하는 InterruptedException로 인터럽트 여부를 구분하는 경우가 있다.
하지만 interrupt() 호출시 InterruptedException는 항상 발생하는 것이 아니다.

InterruptedException은 interrupt() 호출 당시 해당 Thread가 block 되거나 특정 상태에서만 발생한다.

즉, 잘 구동중인 Thread는 interrupt()가 호출된다고 해서 InterruptedException이 발생하지는 않는다.

1.2. Thread 종료를 위한 안전장치

  1. flag 적용
    Thread의 while() 조건에 boolean flag를 설정한다.
    해당 Thread를 중간에 멈추고자 할때 flag 값을 수정한다.

    boolean flag = true;
    public void run() {
        while(flag) {
            ...
        }		
    }
    	
    public void setFlag(boolean flag) {
        this.flag = flag;
    }
    
  2. sleep() 추가하여 아주 잠깐 대기시키기
    Thread의 마지막에 sleep()을 잠시 줘서 대기상태로 만든다.
    만약 interrupt()가 호출된다면 잠시 대기상태로 가있는 동안 InterruptException이 발생할 것이다.
    이 방법은 약간의 성능 저하가 발생한다.

    public void run() {
        while(flag) {
    			
            try{
                // 1 nano초 만큼 Sleep
                Thread.sleep(0,1);
            }catch(Exception e) {}
        }		
    }
    

1.3. isInterrupted() 사용 주의 점

Thread 종료를 위한 안전장치로 flag를 언급했는데 사실 이미 flag는 존재한다.

interrupt()가 호출되면 내부적으로 플래그를 설정하여 인터럽트 여부를 설정하게 되고 이 설정 여부는 isInterrupted()로 알 수 있다.

public class SimpleThread extends Thread {
    public void run() {
        while(!isInterrupted()) {
            ...
        }
    }
}

isInterrupted()는 초기화 될 수 있다는 점에서 주의해야 한다.
예를 들어 interrupt() 호출 시 InterruptedException이 발생하면 이 flag값은 다시 초기화 된다.

public class SimpleThread extends Thread {
    public void run() {
        try{
            Thread.sleep(10000);	
        }catch(InterruptedException e) {
            // InterruptedException이 발생하면 flag가 초기화됨.
        }
		
        // sleep 중간에 interrupt()를 호출했을때 이 결과는 true일것 같지만 false가 리턴됨.
        Log.d(TAG, "isInterrupted() : " + isInterrupted());
    }
}

이렇게 InterruptedException가 발생했는데 이 Exception을 쓰레드 내부에서 catch하면 외부에서는 이 쓰레드가 interrupted 됬는지 확인할 수 있는 방법이 없다. (flag를 초기화 시켜버리기 때문에)

이 때는 catch절에서 한번 더 interrupt() 시키는 방법이 있을 수 있다.

public class SimpleThread extends Thread {
    public void run() {
        try{
            Thread.sleep(10000);	
        }catch(InterruptedException e) {
            // InterruptedException이 발생하면 flag가 초기화됨.
            // 한번 더 interrupt() 호출
            Thread.currentThread().interrupt();
        }
		
        // 한번 더 interrupt()를 해줘서 true로 리턴됨.
        Log.d(TAG, "isInterrupted() : " + isInterrupted());
    }
}

2. notify(), notifyAll()

wait()에 의해 대기된 Thread를 깨우는 메서드이다.
두 메서드는 Object에 정의된 메서드로 notify()는 객체의 모니터와 관련된 단일 Thread를 깨우고, notifyAll()은 객체의 모니터와 관련있는 모든 Thread를 깨운다.

카테고리:

업데이트:

댓글남기기