通過鎖順序來避免動態的鎖順序死鎖
阿新 • • 發佈:2018-12-01
通過鎖順序來避免動態的鎖順序死鎖
歡迎關注作者部落格
簡書傳送門
前言
兩個執行緒試圖通過不同的順序獲取多個相同的鎖。如果請求的順序不相同,那麼會出現迴圈的鎖依賴現象,產生死鎖。但是如果保證同時請求鎖L和鎖M的每一個執行緒,都是按照從 L 到 M 的順序,那麼就不會發生死鎖了。
比如:銀行賬戶轉賬問題,兩個使用者轉賬的話,如果採用一般的synchronized巢狀的話,容易造成死鎖。
思想
我們可以制定鎖的順序,並在整個應用程式中,獲得鎖都必須始終遵守這個既定的順序。我們在制定物件順序的時候,可以使用System.identityHashCode這樣一種方式,它會返回Object.hashcode所返回的值。 在極少數的情況下,2個物件具有相同的雜湊碼,我們必須使用任意的中數來決定鎖的順序,這又重新引入了死鎖的可能性。這個時候我們使用另一個鎖(加時賽鎖),在獲得2個物件的鎖之前,就要獲得這個鎖。
/**
* @program:
* @description: 動態的鎖順序死鎖的解決方案——通過鎖順序來避免死鎖
* @author: zhouzhixiang
* @create: 2018-11-16 20:03
*/
public class ThreadTest5 {
// 加時賽鎖
private static final Object tieLock = new Object();
class Account implements Comparable{
String username;
String password;
long moneycount;
void debit(long amount) {
moneycount = moneycount - amount;
}
void credit(long amount) {
moneycount = moneycount + amount;
}
@Override
public int compareTo(Object o) {
return (int) (moneycount - ((long)o));
}
}
/**
* 此方法容易發生動態的鎖順序死鎖——錯誤方式
* @param fromAccount 轉賬方
* @param toAccount 收賬方
* @param amount 轉賬金額
*/
public void tranferMoney(Account fromAccount, Account toAccount, long amount) throws InsufficientResourcesException {
synchronized(fromAccount) {
synchronized (toAccount) {
if(fromAccount.compareTo(amount) < 0)
throw new InsufficientResourcesException();
else {
fromAccount.debit(amount);
toAccount.credit(amount);
}
}
}
}
/**
* 通過鎖順序來避免死鎖——正確方法
* @param fromAccount 轉賬方
* @param toAccount 收賬方
* @param amount 轉賬金額
*/
public void tranferMoney2(Account fromAccount, Account toAccount, long amount) throws InsufficientResourcesException {
class Helper {
public void tranfer() throws InsufficientResourcesException {
if(fromAccount.compareTo(amount) < 0)
throw new InsufficientResourcesException();
else {
fromAccount.debit(amount);
toAccount.credit(amount);
}
}
}
int fromHashCode = System.identityHashCode(fromAccount);
int toHashCode = System.identityHashCode(toAccount);
if(fromHashCode < toHashCode) {
synchronized (fromAccount) {
synchronized (toAccount) {
new Helper().tranfer();
}
}
}else if (fromHashCode > toHashCode) {
synchronized (toAccount) {
synchronized (fromAccount) {
new Helper().tranfer();
}
}
}else {
// 加時賽鎖
synchronized (tieLock) {
synchronized (fromAccount) {
synchronized (toAccount) {
new Helper().tranfer();
}
}
}
}
}
}
歡迎加入Java猿社群