Spurious Wakeup in Java

http://java.sun.com/j2se/1.5.0/docs/api/java/lang/Object.html#wait(long)

자바에서 Object.wait( ) 를 호출할때는 다음과 같이 해야한다는 것은 상식입니다.

synchronized (obj) {
    while (<condition does not hold>)
        obj.wait(timeout);
    ... // Perform action appropriate to condition
}

문제는 임의의 쓰레드를 Object.notify 로 깨울때도 위의 코드처럼 while이 필요한가 입니다. 그걸 논의 하기에 앞서 먼저 notify를 부를까 아니면 notifyAll을 부를까의 문제가 있습니다. 하나는 단일 쓰레드만 깨우고 하나는 모든 wait 중인 쓰레드를 깨웁니다. notify를 하면 하나의 쓰레드만 깨워지지만 어떤 쓰레드를 깨울까는 알 수 없습니다. wait중에 notify 받은 쓰레드는 깨나지만 notify한 측이 synchronized블럭을 빠져나올때까지 실행될 기회를 얻지못하며, notify한 측이 블럭을 빠져나왔다 하더라도 그 다음 순서가 반드시 notify 받은 쓰레드일 필요는 없습니다.

어쨌든, notify를하게 될 경우의 문제는 다수의 쓰레드가 하나의 객체에 synchornized 를 걸고 있을 때, 그들 중 한녀석만 notify를 받게 될 우려가 있다는 것입니다. 한마디로 stravation 되는 녀석이 발생할 수 있죠. 그런 이유로 notifyAll을 하면 starvation을 대충 (완전히는 아니죠) 막을 수 있고, 이상한 쓰레드 하나가 자원을 모두 먹는 일을 막습니다.

다시 원래의 문제로 돌아와서, notifyAll이라면 모든 쓰레드가 한꺼번에 깨나므로 걔네들은 condition 검사가 while이 아니라 if로 되고 있다면 모두 “Perform action appropriate..” 부분을 수행하여 논리적 오류가 발생하게 됩니다. 이걸 막으려면 while문으로 조건을 주어야 딱 한녀석만 조건을 만족하면서 작업을 수행하게 되죠.

그러나 notify할때는 단 하나의 쓰레드만 깨날 것이므로 이런 while 문이 필요 없을거라고 생각할 수 있으나 이는 틀린 정보입니다.

A thread can also wake up without being notified, interrupted, or timing out, a so-called spurious wakeup. While this will rarely occur in practice, applications must guard against it by testing for the condition that should have caused the thread to be awakened, and continuing to wait if the condition is not satisfied.

이렇게 단 하나의 쓰레드만 깨울 수 없는 이유는 POSIX 쓰레드의 근본적인 원리와 맞닿아 있는 듯 합니다. 관련된 설명은 Doug Lea의 책과 Joshua Bloch 의 책에 나와있다는데, 일단 후자인 Effective Java에서는 원리에 대해서 나와있지는 않습니다. (POSIX 표준에 대한 링크만 있음) 누군가 아시면 설명해주시면 감사 ㅎㅎ 아무도 안알려주시면 제가 알아보죠 뭐;;;

Similar Posts:

Comments 3