多線程-6.死鎖的四個產生條件以及破壞死鎖的方法


產生死鎖的四個必要條件:
  1.互斥條件:一個資源同一時刻只能被一個線程所占有。
  2.持有並等待條件:一個線程T1已經持有某資源X,然后申請獲得新的資源Y,在等待過程中不釋放已有資源X。
  3.不可搶占條件:其它線程不能強行搶占線程T1的資源。
  4.循環等待條件:線程T1持有資源X,等待線程T2持有的資源Y,線程T2等待X。
死鎖的例子
  假設線程 T1 執行賬戶 A 轉賬戶 B 的操作,賬戶 A.transfer(賬戶 B);同時線程 T2 執行賬戶 B 轉賬戶 A 的操作,賬戶 B.transfer(賬戶 A)。當 T1 和 T2 同時執行完①處的代碼時,T1 獲得了賬戶 A 的鎖(對於 T1,this 是賬戶 A),而 T2 獲得了賬戶 B 的鎖(對於 T2,this 是賬戶 B)。之后 T1 和 T2 在執行②處的代碼時,T1 試圖獲取賬戶 B 的鎖時,發現賬戶 B 已經被鎖定(被 T2 鎖定),所以 T1 開始等待;T2 則試圖獲取賬戶 A 的鎖時,發現賬戶 A 已經被鎖定(被 T1 鎖定),所以 T2 也開始等待。於是 T1 和 T2 會無期限地等待下去,也就是我們所說的死鎖了。
 1 class Account {
 2   private int balance;
 3   // 轉賬
 4   void transfer(Account target, int amt){
 5     // 鎖定轉出賬戶
 6     synchronized(this){     ①
 7       // 鎖定轉入賬戶
 8       synchronized(target){ ②
 9         if (this.balance > amt) {
10           this.balance -= amt;
11           target.balance += amt;
12         }
13       }
14     }
15   } 
16 }
破壞死鎖的方法:
  互斥條件:不可改變。
  持有並等待條件:可以一次性給線程申請所有的資源。
  不可搶占條件:線程在申請不到資源時主動釋放掉已有的資源。
  循環等待條件:將資源線性排列,申請時先申請序號小的,再申請序號大的。
破壞不可搶占條件:
  synchronized 申請資源的時候,如果申請不到,線程直接進入阻塞狀態了,而線程進入阻塞狀態,啥都干不了,也釋放不了線程已經占有的資源。Java 在語言層次無法解決這個問題,不過在 SDK 層面還是解決了的。java.util.concurrent 這個包下面提供的 Lock 是可以輕松解決這個問題的。
破壞循環等待條件:
  假設每個賬戶都有不同的屬性 id,這個 id 可以作為排序字段,申請的時候,可以按照從小到大的順序來申請。比如下面代碼中,①~⑥處的代碼對轉出賬戶(this)和轉入賬戶(target)排序,然后按照序號從小到大的順序鎖定賬戶。這樣就不存在“循環”等待了。
  
 1 class Account {
 2   private int id;
 3   private int balance;
 4   // 轉賬
 5   void transfer(Account target, int amt){
 6     Account left = this 7     Account right = target;    ②
 8     if (this.id > target.id) { ③
 9       left = target;           ④
10       right = this;            ⑤
11     }                          ⑥
12     // 鎖定序號小的賬戶
13     synchronized(left){
14       // 鎖定序號大的賬戶
15       synchronized(right){ 
16         if (this.balance > amt){
17           this.balance -= amt;
18           target.balance += amt;
19         }
20       }
21     }
22   } 
23 }


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM