Тупиковая ситуация с потоками Java

#multithreading #deadlock

#многопоточность #взаимоблокировка

Вопрос:

Я читаю Java Concurrency на практике и застрял в этой программе, где автор говорит, что это не приведет к взаимоблокировке. Но если я поменяю местами аргументы метода, это вызовет взаимоблокировку.

 Obj1 = hash-code =1
Obj2 = hash-code =2
  

Thread1. transfer(Obj1, Obj2) получит блокировку от fromAcct и будет ожидать блокировки toAcct

Thread2.transfer(Obj2, Obj1) получит блокировку toAcct и будет ожидать блокировки fromAcct

Итак, по сути, мы оказались в тупике.

Мой вопрос в том, как получается, что в следующем коде избегается мертвая блокировка.

 public class InduceLockOrder {
    private static final Object tieLock = new Object();

    public void transferMoney(final Account fromAcct,
                              final Account toAcct,
                              final DollarAmount amount)
            throws InsufficientFundsException {
        class Helper {
            public void transfer() throws InsufficientFundsException {
                if (fromAcct.getBalance().compareTo(amount) < 0)
                    throw new InsufficientFundsException();
                else {
                    fromAcct.debit(amount);
                    toAcct.credit(amount);
                }
            }
        }
        int fromHash = System.identityHashCode(fromAcct);
        int toHash = System.identityHashCode(toAcct);

        if (fromHash < toHash) {
            synchronized (fromAcct) {
                synchronized (toAcct) {
                    new Helper().transfer();
                }
            }
        } else if (fromHash > toHash) {
            synchronized (toAcct) {
                synchronized (fromAcct) {
                    new Helper().transfer();
                }
            }
        } else {
            synchronized (tieLock) {
                synchronized (fromAcct) {
                    synchronized (toAcct) {
                        new Helper().transfer();
                    }
                }
            }
        }
    }

    interface DollarAmount extends Comparable<DollarAmount> {
    }

    interface Account {
        void debit(DollarAmount d);

        void credit(DollarAmount d);

        DollarAmount getBalance();

        int getAcctNo();
    }

    class InsufficientFundsException extends Exception {
    }
}
  

Комментарии:

1. У вас есть конкретный вопрос по этому поводу? Из того, что вы сейчас опубликовали, неясно.

2. Это пример из учебника о том, как создать взаимоблокировку. Получение блокировок в том же порядке.

3. Это правильно, это решение, позволяющее избежать взаимоблокировки, но оно не решает эту проблему.

Ответ №1:

В вашем примере указано, что это на самом деле правильно.

Случай 1: Thread1.transfer(Obj1, Obj2)

В этом случае, fromHash < toHash таким образом, первая блокировка относится к fromAcct параметру or Obj1 , а вторая — к toAcct параметру or Obj2

Случай 2: Thread1.transfer(Obj2, Obj1)

В этом случае, fromHash > toHash таким образом, первая блокировка относится к toAcct параметру or Obj1 , а вторая — к fromAcct параметру or Obj2

Порядок блокировки тот же, поэтому взаимоблокировки не происходит.