Java - thread의 run() 메소드가 synchronized 메소드일 때

Java에서 어떤 Class Foo가 Runnable 인터페이스를 구현하거나 Thread 클래스를 상속받아 run() 메소드를 가지는데, 이 때 run() 을 synchronized 메소드로 만들었다고 하자. 어떤 문제가 생기는가?

1) Thread-X (내지는 main thread) 가 Foo 의 instance인 foo 를 만들어 명시적 또는 묵시적으로 run을 호출한다.synchronized method 는 묵시적으로 synchonized(this) 를 함의하므로, foo 객체에 대한 lock 은 thread-X 가 가지게 된다. 따라서 X 는 foo 의 lock을 들고 있는 상태로 자기 일을 계속 수행하게 될 것이다.

2) foo의 run()은 임무가 끝날때 까지 종료되지 않으므로 Thread-X 는 이 lock 을 계속(!) 붙들고 있게 된다. 언제까지? 당연히 run()이 종료될 때 까지.

3) 문제는 다른 Thread-Y 가 Foo 의 다른 public synchronized 메소드를 호출하거나, 혹은 private synchronized 메소드를 '내부적으로' 호출하는 public 메소드를 호출하려고 할 때 일어난다. 어느쪽이든 synchonzied 메소드가 호출되는 시점에서 foo 객체의 lock 을 얻으려고 하기 때문이다.

4) 그런데, foo 에는 이미 lock 이 걸려있어서 Thread-X가 lock을 놓기 전에 (즉 foo 가 아직 '돌고' 있는 동안에)는 다른 synchronized method는 lock이 풀릴 때 까지 기다릴 수 밖에 없다. 그런데 돌고 있는 thread가 언제 끝날지는 아무도 모른다. 며느리도 모른다. 어쩌라고!

5) 특히 3)의 후자의 경우. 이 경우엔 synchonized 라는 사실이 명시적으로 드러나지 않기 때문에, 외부에서 다른 lock을 걸고 들어오거나 thread-safe 메소드로 가정해서 접근하려는 경우엔 문제가 복잡해진다. Thread-Y 모듈의 담당자는 (자기가 모르는) X와의 경쟁이 아니라 다른 Y', Y'' 또는 Z 와의 경쟁 관계에서 Deadlock이 걸리는 것으로 착각하고 문제를 매우 복잡하게 이해하게 된다. 사실은 이렇게 단순하고 엉뚱한 문제인데!

따라서, 결론적으로... 웬만하면 run은 synchronized method로 만들지 말고, run을 방어하기 위한 별도의 lock object를 잡는 것이 좋다. (이런 식의 비슷한 문제들을 피하기 위해 synchronized 키워드로 대충 푸는 대신 '명시적인' lock 객체를 준비하는 습관을 들이자) 또한 Java5 부터 지원되는 java.util.concurrent 패키지는 1.4 에도 백포팅되어있으며, 널리 쓰이는 여러 패턴들 (Read-Write Lock, Blocking Queue, Thread Pool 등) 에 대한 '믿을만한' 구현체가 포함되어 있으므로, 고통받는 뇌세포들에게 오늘 저녁 메뉴를 고민할 시간을 주려면 버리지 말고 활용하도록 하자.

by 가짜집시 | 2007/03/22 17:50 | 0 1 Nation | 트랙백 | 덧글(0)

트랙백 주소 : http://lunaris.egloos.com/tb/1532377
☞ 내 이글루에 이 글과 관련된 글 쓰기 (트랙백 보내기) [도움말]

:         :

:

비공개 덧글

◀ 이전 페이지          다음 페이지 ▶