[本文是我對Java Concurrency In Practice C10的歸納和總結(jié). ?轉(zhuǎn)載請注明作者和出處, ?如有謬誤, 歡迎在評論中指正. ]
如果多個線程以不同的順序持有多個鎖, 可能發(fā)生死鎖:
?
public class AccountTrans {
public 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方法先后鎖定fromAccount和toAccount對象. 如果2個線程以如下的方式調(diào)用transferMoney方法:
A: transferMoney(myAccount, yourAccount, 10);?
B: transferMoney(yourAccount, myAccount, 20);
死鎖有可能就會發(fā)生.
關(guān)鍵在于需要保證以相同的順序獲取多個鎖:
?
public class AccountTrans {
// 額外的鎖
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);
}
}
}
// 計算fromAcct和toAcct的hashCode值
int fromHash = System.identityHashCode(fromAcct);
int toHash = System.identityHashCode(toAcct);
// 根據(jù)hashCode值確定獲取鎖的順序
if (fromHash < toHash) {
synchronized (fromAcct) {
synchronized (toAcct) {
new Helper().transfer();
}
}
} else if (fromHash > toHash) {
synchronized (toAcct) {
synchronized (fromAcct) {
new Helper().transfer();
}
}
} else {
// 當hashCode值相同時, 無法確定fromAcct和頭Acct鎖的獲取順序, 因此增加額外的鎖
synchronized (tieLock) {
synchronized (fromAcct) {
synchronized (toAcct) {
new Helper().transfer();
}
}
}
}
}
}
?
open call
所謂open call是指在未持有鎖時調(diào)用外部方法. 持有鎖的時候調(diào)用外部方法, 如果被調(diào)用的方法需要獲取其他的鎖, 可能帶來死鎖的風險. 如果被調(diào)用的方法發(fā)生阻塞, 當前線程將長時間持有鎖, 其他等待獲取該鎖的線程就會被阻塞.
因此我們應該盡量在未持有鎖的時候進行方法的調(diào)用.
?
資源死鎖
比如線程A持有數(shù)據(jù)庫D1的連接, 并等待獲取數(shù)據(jù)庫D2的連接. 而線程B持有數(shù)據(jù)庫D2的連接, 并等待獲取數(shù)據(jù)庫D1的連接. 此時就發(fā)生了死鎖.
資源死鎖的另一種形式是線程饑餓死鎖, 參見第八章.
?
避免死鎖
1. 盡量不要同時持有多個鎖.
2. 如果必須同時持有多個鎖, 那么保證以一致的順序獲取鎖.
3. 盡量在未持有鎖的情況下進行方法的調(diào)用(open call).