#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
Порядок блокировки тот же, поэтому взаимоблокировки не происходит.