필요한 자원을 모두 다른 곳에서 확보하고 놓지 않기 때문에 모두가 서로 상대방 자원을 놓기만을 기다리는 상황
public class LeftRightDeadlock {
private final Object left = new Object();
private final Object right = new Object();
public void leftRight() {
synchronized (left) {
synchronized (right) {
doSomething();
}
}
}
public void rightLeft() {
synchronized (right) {
synchronized (left) {
doSomethingElse();
}
}
}
void doSomething() { }
void doSomethingElse() { }
}
// Warning: deadlock-prone!
public static void transferMoney
(
Account fromAccount,
Account toAccount,
DollarAmount amount
) throws InsufficientFundsException
{
synchronized (fromAccount) {
synchronized (toAccount) {
if (fromAccount.getBalance().compareTo(amount) < 0) {
throw new InsufficientFundsException();
} else {
fromAccount.debit(amount);
toAccount.credit(amount);
}
}
}
}
transferMoney(myAccount, yourAccount, 10);
transferMoney(yourAccount, myAccount, 20);
결국 서로 다른 순서로 락을 잡는 것과 동일한 효과. System.identityHashCode 를 사용하여 락 순서를 결정하는 방법도 있긴 하지만 데드락의 위험성이 없어진 것은 아님.
다른 작업의 실행 결과를 확인해야만 하는 작업이 있다면 스레드 소모성 데드락의 원인이 되기 쉽다.
(자바 병렬 프로그래밍 8.1.1 절 참고)
특정 메서드를 호출할때 해당 메서드를 호출하는 메서드를 알지 못해야 한다.
락을 전혀 확보하지 않은 상태에서 메서드를 호출하는 것은 오픈호출이라 한다. 만약 호출하는 메서드가 락을 걸고 메서드를 호출할때 호출당하는 메서드에서 또 중복하여 락을 걸 경우 데드락의 위험이 있기 때문이다.
단순히 간편하다는 이유만으로 메서드 전체에 syncronized 를 거는 것은 이래서 위험하다. 필요한 부분만 락을 잡는 다는 것은 병렬 프로그래밍의 기본