기본적인 쓰레드 제어 - sleep / wait / notify / notifyAll

13 January 2015

기본적인 쓰레드 제어 메소드인 sleep, wait, notify, notifyAll에 대해서 간략하게 살펴보자.

쓰레드의 일시 정지 - sleep

public class Main {
    public static void main(String[] args) {
 
        for (int i = 0; i < 10; i++) {
            System.out.print("Good!");
            try {
                Thread.sleep(1000);		// 1000밀리초=1초동안 현재의 쓰레드를 일시정지함
            } catch (InterruptedException e) {
            }
        }
 
    }
}

Good!을 출력한 뒤, sleep() 메소드 호출로 인하여 해당 쓰레드가 1초(1000밀리초) 동안 일시정지 된다. 1초가 지난 뒤에는 스스로 깨어나서, 수행하던 코드를 마저 수행한다. 따라서 1초 간격으로 “Good!”이 출력되게 된다.

참고) Thread.sleep으로 잠들어 있는 쓰레드를 도중에 깨우려면 interrupt를 발생시켜야 한다.

public static void sleep(long millis) throws InterruptedException
public static void sleep(long millis, int nanos) throws InterruptedException

sleep() 메소드는 InterruptedException을 발생시킬 수 있으므로 try-catch로 묶었다. 밀리초 뿐 아니라 나노초를 파라미터로 받는 오버로딩 메소드도 있다. 하지만 실질적으로 그렇게까지 세세한 제어는 되지 않는다.

쓰레드의 대기 - wait

모든 인스턴스들은 각각 wait 집합을 가지고 있다. wait() 메소드를 실행한 후 대기중인 쓰레드들의 (가상의, 개념적인) 집합이다.

wait();          // 이 둘은 동일함
this.wait();

락을 획득하여 실행되고 있던 쓰레드가 wait() 메소드를 만나면 동작을 정지하고, wait 집합에 들어가고, 가지고 있던 락을 해제한다. 바꿔 말하자면, 락을 가지고 있는 쓰레드만 wait() 메소드를 실행할 수 있다.

그리고 다음 중 어느 한 상황이 발생하기 전까지는 영원히 wait 집합 안에서 잠들어 있는다.

  • 다른 쓰레드의 notify() 메소드 호출에 의해 깨어난다.
  • 다른 쓰레드의 notifyAll() 메소드 호출에 의해 깨어난다.
  • 다른 쓰레드의 interrupt() 메소드 호출에 의해 깨어난다.
  • wait() 메소드가 타임아웃 된다.

대기 중인 쓰레드 한 개 깨우기 - notify

락을 획득하여 실행되고 있던 쓰레드가 notify() 메소드를 호출하면, 해당 인스턴스의 wait 집합에 있는 쓰레드 한 개가 wait 집합에서 꺼내진다. 이 때 wait 집합 안에 있는 쓰레드 중 어떤 쓰레드가 선택될지는 정해져 있지 않다. wait와 마찬가지로, 락을 가지고 있는 쓰레드만 notify() 메소드를 호출할 수 있다.

instanceName.notify();

notify() 호출에 의하여 wait 집합에서 나온 쓰레드가 곧바로 실행을 재개하는 것은 아니다. 실행을 재개하려면 wait 집합에 들어갈 때 해제했던 락을 다시 획득해야 하는데.. notify()가 호출된 순간에는 notify()를 실행한 쓰레드가 해당 락을 가지고 있는 상태이기 때문이다.

대기 중인 모든 쓰레드 깨우기 - notifyAll

notifyAll();		// this.notifyAll()과 동일함
this.notifyAll();	// notifyAll()과 동일함
instanceName.notifyAll();

락을 획득하여 실행되고 있던 쓰레드가 notifyAll() 메소드를 호출하면, 해당 인스턴스의 wait 집합에 있는 모든 쓰레드를 wait 집합에서 꺼낸다. wait, notify와 마찬가지로, 락을 가지고 있는 쓰레드만 nodifyAll() 메소드를 호출할 수 있다.

wait() & notify() & notifyAll()

  1. 위에서도 언급했듯이 wait, notify, notifyAll 메소드는 전부 ‘락을 가지고 있던 쓰레드’만이 호출할 수 있다. 락을 가지고 있지 않은 쓰레드가 wait(), notify(), notifyAll() 메소드를 호출할 경우는 java.lang.IlegalMonitorStateException이 발생한다.
  2. wait, notify, notifyAll 메소드는 java.lang.Object 클래스의 메소드이지 Thread 클래스의 메소드가 아니다. 이 세개의 메소드는 쓰레드에 대한 조작이라고 하기 보다는, 인스턴스의 wait 집합에 대한 조작이다. wait 집합은 모든 인스턴스가 가지고 있으므로 wait, notify, notifyAll은 Object 클래스의 메소드인 것이다. (Object 클래스는 모든 클래스의 슈퍼클래스이므로, 엄밀히 말하자면 Thread 클래스의 (슈퍼클래스의) 메소드이다.)
MySQL 쿼리를 오라클 쿼리로 변경하기 InterruptedException과 멀티쓰레드