爲了保證線程的安全,咱們引入了加鎖機制,可是若是不加限制的使用加鎖,就有可能會致使順序死鎖(Lock-Ordering Deadlock)。上篇文章咱們也提到了在線程詞中由於資源的不足而致使的資源死鎖(Resource Deadlock)。java
本文將會討論一下順序死鎖的問題。git
咱們來討論一個常常存在的帳戶轉帳的問題。帳戶A要轉帳給帳戶B。爲了保證在轉帳的過程當中A和B不被其餘的線程意外的操做,咱們須要給A和B加鎖,而後再進行轉帳操做, 咱們看下轉帳的代碼:github
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都加了鎖,程序應該能夠很完美的按照咱們的要求來執行。安全
那若是咱們考慮下面的一個場景:ide
A:transferMoneyDeadLock(accountA, accountB, 20)
B:transferMoneyDeadLock(accountB, accountA, 10)
複製代碼
若是A和B同時執行,則可能會產生A得到了accountA的鎖,而B得到了accountB的鎖。從然後面的代碼沒法繼續執行,從而致使了死鎖。spa
對於這樣的狀況,咱們有沒有什麼好辦法來處理呢?線程
加入無論參數怎麼傳遞,咱們都先lock accountA再lock accountB是否是就不會出現死鎖的問題了呢?code
咱們看下代碼實現:ci
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,從而保證同一時間只有一個線程可以運行內部的方法,從而保證了任務的執行而不產生死鎖。
本文的例子能夠參考github.com/ddean2009/l…
更多內容請訪問 flydean的博客