관련 이론 및 문법 - 데드락 (deadlock)
데드락은, 두 개 이상의 쓰레드들이 동기화된 블럭 (동시에 하나의 쓰레드만 접근할 수 있는 블럭) 에서 자신의 실행을 멈추고 서로의 실행을 기다리다가 막힌 상태를 말합니다. 먼저, 동기화된 블럭에 들어간 쓰레드가 블럭의 락을 얻기 때문에, 다른 쓰레드는 락이 없어서 그 블럭에 들어가지 못하고 대기하게 되는 것입니다. 쓰레드의 처리가 정상적으로 끝나면, 락을 내려놓고 블럭을 빠져나와야 하는데, 비정상적으로, 실행을 제대로 끝마치지 못해서, 락을 계속해서 점유하고 있게 되어 다른 쓰레드는 계속 대기하게 됩니다.
Java 또한, 멀티 쓰레드를 지원하지만, 데드락 또한 발생의 여지가 있기 때문에 주의해서 코딩하여야 합니다. 잘못 사용된 동기화된 블럭들이 여러 곳에서 정의되어 동시에 실행될 수 있으므로, 데드락 또한 동시에 여러 곳에서 발생 가능합니다. 데드락을 해결하기 위해서는, 주로 데드락된 쓰레드를 종료 혹은 정지시켜서, 다른 쓰레드에게 실행권을 넘겨야 합니다.
실행권을 넘기는 방법으로는, Object 의 wait 메소드와 Thread 의 yield 메소드를 통해서 현재 블럭 구역 내에서 실행중인 쓰레드의 상태를 변경시키는 것입니다.. wait 메소드는 그 코드를 수행하던 쓰레드를 휴지 상태로 만들면서 동시에 쓰레드가 가지고 있던 lock 을 내려놓습니다 . 이는 notify, notifyAll 메소드에 의해서 깨워야 합니다. yield 메소드는 현재 쓰레드를 대기 상태로 만들고, lock 을 내려놓지는 않고, 다음 쓰레드가 진입할 수 있게 해줍니다.
Thread.sleep 메소드는 lock 을 내려놓지 않고, 단순히 일정 시간 동안 현재 쓰레드의 작업을 delay 시키는 역할을 합니다. 결과적으로, wait 메소드만 lock 을 내려놓고, 그 외의 메소드들 (sleep, join, yield 등) 은 lock 을 내려놓지 않습니다. lock 을 내려놓지 않아도 된다는 것은, 비동기화된 블럭에서도 호출될 수 있다는 이야기 입니다. (lock 은 동기화된 블럭에 들어갈 때 가져와야 하는 것이기 때문에) 역으로, wait 와 이를 깨우는 notify 와 notifyAll 메소드는 동기화된 블럭에서만 호출되어야 한다는 이야기 입니다.
wait, notify, notifyAll 메소드들을 동기화 구역에서만 호출해야 하는 직접적인 이유는 다음과 같습니다.
-> 비동기 구역에서 이 메소드들을 호출하면, IllegalMonitorStateException 이 발생하기 때문입니다.
-> wait 와 notify 메소드 사이에 잠재적인, race condition (여러 쓰레드가 같은 기억 장소를 접근 할때 그들 사이의 경쟁에 의해 수행 결과를 예측할 수 없게 되는 것) 이 발생할 수 있기 때문입니다.
(Thread 클래스 참고)
http://docs.oracle.com/javase/8/docs/api/java/lang/Thread.html
(Object 클래스 참고)
http://docs.oracle.com/javase/8/docs/api/java/lang/Object.html