淺談Java由於不當的執行順序導致的死鎖
我們來討論一個經常存在的賬戶轉賬的問題。賬戶A要轉賬給賬戶B。為瞭保證在轉賬的過程中A和B不被其他的線程意外的操作,我們需要給A和B加鎖,然後再進行轉賬操作, 我們看下轉賬的代碼:
public void transferMoneyDeadLock(Account from,Account to, int amount) throws InsufficientAmountException { synchronized (from){ synchronized (to){ transfer(from,to,amount); } } } private void transfer(Account from,Account to, int amount) throws InsufficientAmountException { if(from.getBalance() < amount){ throw new InsufficientAmountException(); }else{ from.debit(amount); to.credit(amount); } }
看起來上面的程序好像沒有問題,因為我們給from和to都加瞭鎖,程序應該可以很完美的按照我們的要求來執行。
那如果我們考慮下面的一個場景:
A:transferMoneyDeadLock(accountA, accountB, 20) B:transferMoneyDeadLock(accountB, accountA, 10)
如果A和B同時執行,則可能會產生A獲得瞭accountA的鎖,而B獲得瞭accountB的鎖。從而後面的代碼無法繼續執行,從而導致瞭死鎖。
對於這樣的情況,我們有沒有什麼好辦法來處理呢?
加入不管參數怎麼傳遞,我們都先lock accountA再lock accountB是不是就不會出現死鎖的問題瞭呢?
我們看下代碼實現:
private void transfer(Account from,Account to, int amount) throws InsufficientAmountException { if(from.getBalance() < amount){ throw new InsufficientAmountException(); }else{ from.debit(amount); to.credit(amount); } } public void transferMoney(Account from,Account to, int amount) throws InsufficientAmountException { int fromHash= System.identityHashCode(from); int toHash = System.identityHashCode(to); if(fromHash < toHash){ synchronized (from){ synchronized (to){ transfer(from,to, amount); } } }else if(fromHash < toHash){ synchronized (to){ synchronized (from){ transfer(from,to, amount); } } }else{ synchronized (lock){ synchronized (from) { synchronized (to) { transfer(from, to, amount); } } } } }
上面的例子中,我們使用瞭System.identityHashCode來獲得兩個賬號的hash值,通過比較hash值的大小來選定lock的順序。
如果兩個賬號的hash值恰好相等的情況下,我們引入瞭一個新的外部lock,從而保證同一時間隻有一個線程能夠運行內部的方法,從而保證瞭任務的執行而不產生死鎖。
以上就是淺談Java由於不當的執行順序導致的死鎖的詳細內容,更多關於Java由於不當的執行順序導致的死鎖的資料請關註WalkonNet其它相關文章!
推薦閱讀:
- 一文帶你搞懂Java中Synchronized和Lock的原理與使用
- Java並發編程多線程間的同步控制和通信詳解
- Java Synchronized鎖升級原理及過程剖析
- 一篇文章讓你徹底瞭解Java可重入鎖和不可重入鎖
- 死磕 java同步系列之synchronized解析