IT_Programming/Java

wait & notify & notifyAll 동작 과정

JJun ™ 2011. 6. 13. 21:13

+. 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 언어로 배우는 디자인 입문 : 멀티쓰레드편

 

 

 

더보기

Thread의 상태

 

Java 5의 Thread.getState() 메소드는 Thread의 현재 상태를

Thread.State Enum으로 리턴한다. Thread.State Enum의 정의는 아래와 같다.

BLOCKED
Thread state for a thread blocked waiting for a monitor lock.
NEW
Thread state for a thread which has not yet started.
RUNNABLE
Thread state for a runnable thread.
TERMINATED
Thread state for a terminated thread.
TIMED_WAITING
Thread state for a waiting thread with a specified waiting time.
WAITING
Thread state for a waiting thread.

BLOCKED 상태는 Sychronized에 의해 점유된 Monitor를 획득하지 못하고

기다리는 상태임을 의미한다.

 

반면 WAITING이나 TIMED_WAITING 상태는 wait 메소드를 이용해 자신이

획득한 Monitor를 반환하고 Waiting Pool에 대기하고 있음을 의미한다.

 

RUNNABLE 상태는 현재 실제로 작업을 할 수 있는 상태이거나

작업을 수행 중인 상태를 의미한다.

이 상태들에 대한 정확한 이해가 Thread들간의 Lock 경합을 이해하는데

필수적이다.

 

만일 특정 Thread가 특정 Object의 Monitor를 장시간점유하고 있다면,

동일한 Monitor를 필요로 하는 다른 모든 Thread들은 BLOCKED 상태에서

대기하게 된다.

 

이 현상이 지나치게 되면 Thread 폭주가 발생하고 자칫 System 장애를

유발하게 된다. 이런 현상은 wait 메소드를 이용해 대기를 하는 경우도

마찬가지이다.

 

특정 Thread가 장시간 notify를 통해 Wait 상태의 Thread들을 깨워주지 않으면

수많은 Thread 들이 WAITING이나 TIMED_WAITING 상태에서 대기하는 현상이

발생하게 된다. 

 

 

 

 

 

'IT_Programming > Java' 카테고리의 다른 글

Java Real-time Heap Dump 생성  (0) 2011.06.22
[펌] IBM JVM 에서 Memory Leak의 발견과 대처  (0) 2011.06.22
Java Security Manager & Java policy 파일 생성  (0) 2011.06.10
GC Dump 보기  (0) 2011.05.19
Java Closure Example  (0) 2011.05.12