3. Implementation/Java
wait & notify & notifyAll
SSKK
2009. 7. 28. 07:27
+. Wait set - 쓰레드 대합실

wait set 은 인스턴스의 wait 메소드를 실행한 후 동작을 정지하고 있는 쓰레드들의 집합이다. 예를 들면 인스턴스마다 가지고 있는 쓰레드의 대합실 같은 존재이다.
쓰레드는 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 다음 순서로 넘어간다.
+. notifyAll 메소드 - 모든 쓰레드를 wait 셋에서 꺼낸다.
notifyAll 메소드를 사용하면 wait 셋에 있는 모든 쓰레드를 wait 셋에서 꺼낸다. -> 하지만 이 말이 모든 쓰레드가 수행을 시작한다는 의미는 아니다. 락은 이미 notifyAll 을 호출한 쓰레드가 가지고 있으므로 notifyAll 호출 쓰레드가 락을 해제할 때까지 블락된 상태가 된다. 락이 해제된 후, wait 셋에 있는 쓰레드들 중 락을 취한 쓰레드만이 수행을 재개한다.
notifyAll 을 호출하여 wait 셋에 있는 쓰레드들을 깨운다.
Wait 셋에서 깨어난 쓰레드는 인스턴스의 락을 취하기 위해 블락된다.
notiffyAll을 호출한 쓰레드가 락의 소유권을 해제한다.
블락되어 있는 쓰레드들 중 락을 소유한 쓰레드만이 수행을 재개한다.
다시 다음과 같이 wait 셋이 구성 될 것이다.
+. wait, notify, notifyAll 은 Object 클래스의 메소드
wait, notify, notifyAll 의 동작을 떠올려 보자.
참고 : Java 언어로 배우는 디자인 입문 : 멀티쓰레드편


쓰레드는 wait 메소드를 실행하면 동작을 일시 정지하고 wait 셋이라고 하는 대합실로 들어간다. 쓰레드는 다음중 어느 한 상황이 발생하기 전까지는 영원히 그 wait 셋 안에서 잠들어 있게 된다.
- 다른 쓰레드에서 notify 메소드에 의해 깨어난다.
- 다른 쓰레드에서 notifyAll 메소드에 의해 깨어난다.
- 다른 쓰레드에서 interrupt 메소드에 의해 깨어난다.
- 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 셋에 있는 모든 쓰레드를 깨운다.
참고 : Java 언어로 배우는 디자인 입문 : 멀티쓰레드편