3. Implementation/Java

wait & notify & notifyAll

SSKK 2009. 7. 28. 07:27
+. Wait set - 쓰레드 대합실

wait set 은 인스턴스의 wait 메소드를 실행한 후 동작을 정지하고 있는 쓰레드들의 집합이다. 예를 들면 인스턴스마다 가지고 있는 쓰레드의 대합실 같은 존재이다.

쓰레드는 wait 메소드를 실행하면 동작을 일시 정지하고 wait 셋이라고 하는 대합실로 들어간다. 쓰레드는 다음중 어느 한 상황이 발생하기 전까지는 영원히 그 wait 셋 안에서 잠들어 있게 된다.

  • 다른 쓰레드에서 notify 메소드에 의해 깨어난다.
  • 다른 쓰레드에서 notifyAll 메소드에 의해 깨어난다.
  • 다른 쓰레드에서 interrupt 메소드에 의해 깨어난다.
  • wait 메소드가 타임아웃 된다.
+. wait 메소드 - 쓰레드를 wait 셋에 넣는다.

wait 메소드를 실행하기 위해서는 쓰레드가 락을 가지고 있어야 한다. 그러나 wait 셋에 들어갈 때 쓰레드는 그 인스턴스의 락을 일단 해제한다. 이것을 그림으로 나타내면 아래와 같다.

락을 취한 쓰레드 A 가 wait 메소드를 실행한다.


쓰레드 A는 wait 셋에 들어가 락을 해제한다.


쓰레드 B는 락을 설정할 수 있게 된다.


+. notify 메소드 - 쓰레드를 wait 셋에서 꺼낸다.

notify 메소드를 사용하면 wait 셋에 있는 쓰레드 한 개를 wait 셋에서 꺼낸다. 이 과정을 그림으로 나타내면 아래와 같다.

락을 취한 쓰레드 B가 메소드를 실행한다.



쓰레드 A가 wait 셋에서 나와 wait 다음 순서로 남어가려 하지만 notify 를 실행한 쓰레드 B가 아직 락을 가지고 있다.



notify를 실행한 쓰레드 B가 락을 해제한다.



wait 셋에서 나온 쓰레드 A가 락을 취하고 wait 다음 순서로 넘어간다.



 notify 메소드를 실행했을 때 wait 셋에서 대기 중인 쓰레드가 여러개라고 하자. 이 때 어느 쓰레드가 선택될지는 정해져 있지 않다. 제일 먼저 wait 한 쓰레드가 선택될지 무작위로 선택될지 그 밖의 방법으로 선택될지는 Java 처리계에 따른다. 그러므로 선택된 쓰레드에 의존하여 프로그램을 작성하는 것은 바람직하지 않다.

+. notifyAll 메소드 - 모든 쓰레드를 wait 셋에서 꺼낸다.

notifyAll 메소드를 사용하면 wait 셋에 있는 모든 쓰레드를 wait 셋에서 꺼낸다. -> 하지만 이 말이 모든 쓰레드가 수행을 시작한다는 의미는 아니다. 락은 이미 notifyAll 을 호출한 쓰레드가 가지고 있으므로 notifyAll 호출 쓰레드가 락을 해제할 때까지 블락된 상태가 된다. 락이 해제된 후, wait 셋에 있는 쓰레드들 중 락을 취한 쓰레드만이 수행을 재개한다.

notifyAll 을 호출하여 wait 셋에 있는 쓰레드들을 깨운다.


Wait 셋에서 깨어난 쓰레드는 인스턴스의 락을 취하기 위해 블락된다.


notiffyAll을 호출한 쓰레드가 락의 소유권을 해제한다.


블락되어 있는 쓰레드들 중 락을 소유한 쓰레드만이 수행을 재개한다.


다시 다음과 같이 wait 셋이 구성 될 것이다.



 notify 메소드와 notifyAll 메소드는 많이 비슷하다. 그렇다면 어느 쪽을 써야 할까?
쉽지 않은 선택이다. notify 가 notifyAll 보다 빠르다. 깨울 쓰레드가 적기 때문이다. 그러나 notify 를 사용할 경우 이용 방법에 따라 프로그램이 멈출 위험이 있다. 일반적으로 notifyAll 을 사용한 쪽이 notify 를 사용한 쪽보다 코드가 견고해진다. 그러니까 프로그래머가 코드의 의미와 한계를 충분히 이해하고 있는 경우가 아니라면 notifyAll 을 사용하는 편이 낫다.

+. wait, notify, notifyAll 은 Object 클래스의 메소드

wait, notify, notifyAll 의 동작을 떠올려 보자.

  • obj.wait 은 obj의 wait 셋에 현재의 쓰레드를 넣는다.
  • obj.notify 는 obj의 wait 셋에서 1개의 쓰레드를 깨운다.
  • obj.notifyAll 은 obj 의 wait 셋에 있는 모든 쓰레드를 깨운다.
즉, wait, notify, notifyAll 은 모두 쓰레드에 대한 조작이라기 보다 인스턴스의 wait 셋에 대한 조작이라고 할 수 있다. wait 셋은 모든 인스턴스가 가지고 있으므로 wait, notify, notifyAll 은 Object 클래스의 메소드인 셈이다.

참고 : Java 언어로 배우는 디자인 입문 : 멀티쓰레드편