成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

如何避免死鎖?我們有套路可循

開發 后端
粗粒度鎖我們不提倡,所以會使用細粒度鎖,但使用細粒度鎖的時候,我們要嚴格按照 Coffman 的四大條件來逐條判斷,這樣再應用我們這幾個解決方案來解決就好了。

寫在前面

上一篇文章共享資源那么多,如何用一把鎖保護多個資源? 文章我們談到了銀行轉賬經典案例,其中有兩個問題:

  1.  單純的用 synchronized 方法起不到保護作用(不能保護 target)
  2.  用 Account.class 鎖方案,鎖的粒度又過大,導致涉及到賬戶的所有操作(取款,轉賬,修改密碼等)都會變成串行操作

如何解決這兩個問題呢?咱們先換好衣服穿越回到過去尋找一下錢莊,一起透過現象看本質,dengdeng deng.......

[[280686]]

來到錢莊,告訴柜員你要給鐵蛋兒轉 100 銅錢,這時柜員轉身在墻上尋找你和鐵蛋兒的賬本,此時柜員可能面臨三種情況:

  1.  理想狀態: 你和鐵蛋兒的賬本都是空閑狀態,一起拿回來,在你的賬本上減 100 銅錢,在鐵蛋兒賬本上加 100 銅錢,柜員轉身將賬本掛回到墻上,完成你的業務
  2.  尷尬狀態: 你的賬本在,鐵蛋兒的賬本被其他柜員拿出去給別人轉賬,你要等待其他柜員把鐵蛋兒的賬本歸還
  3.  抓狂狀態: 你的賬本不在,鐵蛋兒的賬本也不在,你只能等待兩個賬本都歸還

放慢柜員的取賬本操作,他一定是先拿到你的賬本,然后再去拿鐵蛋兒的賬本,兩個賬本都拿到(理想狀態)之后才能完成轉賬,用程序模型來描述一下這個拿取賬本的過程:

我們繼續用程序代碼描述一下上面這個模型: 

  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.   }   

這個解決方案看起來很不錯,解決了文章開頭說的兩個問題,但真是這樣嗎?

我們剛剛說過的理想狀態是錢莊只有一個柜員(既單線程)。隨著錢莊規模變大,墻上早已掛了非常多個賬本,錢莊為了應對繁忙的業務,開通了多個窗口,此時有多個柜員(多線程)處理錢莊業務。

[[280687]]

柜員 1 正在辦理給鐵蛋兒轉賬的業務,但只拿到了你的賬本;柜員 2 正在辦理鐵蛋兒給你轉賬的業務,但只拿到了鐵蛋兒的賬本,此時雙方出現了尷尬狀態,兩位柜員都在等待對方歸還賬本為當前客戶辦理轉賬業務。

現實中柜員會溝通,喊出一嗓子 老鐵,鐵蛋兒的賬本先給我用一下,用完還給你,但程序卻沒這么智能,synchronized 內置鎖非常執著,它會告訴你「死等」的道理,最終出現死鎖

Java 有了 synchronized 內置鎖,還發明了顯示鎖 Lock,是不是就為了治一治 synchronized 「死等」的執著呢?😏

解決方案

如何解決上面的問題呢?正所謂知己知彼方能百戰不殆,我們要先了解什么情況會發生死鎖,才能知道如何避免死鎖,很幸運我們可以站在巨人的肩膀上看待問題

Coffman 總結出了四個條件說明可以發生死鎖的情形:

Coffman 條件

互斥條件:指進程對所分配到的資源進行排它性使用,即在一段時間內某資源只由一個進程占用。如果此時還有其它進程請求資源,則請求者只能等待,直至占有資源的進程用畢釋放。

請求和保持條件:指進程已經保持至少一個資源,但又提出了新的資源請求,而該資源已被其它進程占有,此時請求進程阻塞,但又對自己已獲得的其它資源保持不放。

不可剝奪條件:指進程已獲得的資源,在未使用完之前,不能被剝奪,只能在使用完時由自己釋放。

環路等待條件:指在發生死鎖時,必然存在一個進程——資源的環形鏈,即進程集合{P1,P2,···,Pn}中的 P1 正在等待一個 P2 占用的資源;P2 正在等待 P3 占用的資源,……,Pn 正在等待已被 P0 占用的資源。

這幾個條件很好理解,其中「互斥條件」是并發編程的根基,這個條件沒辦法改變。但其他三個條件都有改變的可能,也就是說破壞另外三個條件就不會出現上面說到的死鎖問題

破壞請求和保持條件

每個柜員都可以取放賬本,很容易出現互相等待的情況。要想破壞請求和保持條件,就要一次性拿到所有資源。

作為程序猿你一定聽過這句話:

任何軟件工程遇到的問題都可以通過增加一個中間層來解決

我們不允許柜員都可以取放賬本,賬本要由單獨的賬本管理員來管理

也就是說賬本管理員拿取賬本是臨界區,如果只拿到其中之一的賬本,那么不會給柜員,而是等待柜員下一次詢問是否兩個賬本都在 

  1. //賬本管理員  
  2. public class AccountBookManager {  
  3.     synchronized boolean getAllRequiredAccountBook( Object from, Object to){  
  4.         if(拿到所有賬本){  
  5.             return true;  
  6.         } else{  
  7.             return false;  
  8.         }  
  9.     }  
  10.     // 歸還資源  
  11.     synchronized void releaseObtainedAccountBook(Object from, Object to){  
  12.         歸還獲取到的賬本  
  13.     }  
  14.  
  15. public class Account {  
  16.     //單例的賬本管理員  
  17.     private AccountBookManager accountBookManager;  
  18.     public void transfer(Account target, int amt){  
  19.         // 一次性申請轉出賬戶和轉入賬戶,直到成功  
  20.         while(!accountBookManager.getAllRequiredAccountBook(this, target)){  
  21.             return;  
  22.         }  
  23.         try{  
  24.             // 鎖定轉出賬戶  
  25.             synchronized(this){  
  26.                 // 鎖定轉入賬戶  
  27.                 synchronized(target){  
  28.                     if (this.balance > amt){  
  29.                         this.balance -amt 
  30.                         target.balance += amt;  
  31.                     }  
  32.                 }  
  33.             }  
  34.         } finally {  
  35.             accountBookManager.releaseObtainedAccountBook(this, target);  
  36.         }  
  37.     }  

破壞不可剝奪條件

上面已經給了你小小的提示,為了解決內置鎖的執著,Java 顯示鎖支持通知(notify/notifyall)和等待(wait),也就是說該功能可以實現喊一嗓子 老鐵,鐵蛋兒的賬本先給我用一下,用完還給你 的功能,這個后續將到 Java SDK 相關內容時會做說明

破壞環路等待條件

破壞環路等待條件也很簡單,我們只需要將資源序號大小排序獲取就會解決這個問題,將環路拆除

繼續用代碼來說明: 

  1. class Account {  
  2.   private int id;  
  3.   private int balance;  
  4.   // 轉賬  
  5.   void transfer(Account target, int amt){  
  6.     Account smaller = this          
  7.     Account larger = target;      
  8.     // 排序  
  9.     if (this.id > target.id) {   
  10.       smaller = target;             
  11.       larger = this;              
  12.     }                            
  13.     // 鎖定序號小的賬戶  
  14.     synchronized(smaller){  
  15.       // 鎖定序號大的賬戶  
  16.       synchronized(larger){   
  17.         if (this.balance > amt){  
  18.           this.balance -amt 
  19.           target.balance += amt;  
  20.         }  
  21.       }  
  22.     }  
  23.   }   

當 smaller 被占用時,其他線程就會被阻塞,也就不會存在死鎖了.

附加說明

在實際業務中,關于 Account 都會是數據庫對象,我們可以通過事務或數據庫的樂觀鎖來解決的。另外分布式系統中,賬本管理員這個角色的處理也可能會用 redis 分布式鎖來解決.

在處理破壞請求和保持條件時,我們使用的是 while 循環方式來不斷請求鎖的時候,在實際業務中,我們會有 timeout 的設置,防止無休止的浪費 CPU 使用率

另外大家可以嘗試使用阿里開源工具 Arthas 來查看 CPU 使用率,線程等相關問題,github 上有明確的說明

總結

計算機的計算能力遠遠超過人類,但是他的智慧還需要有帶提高,當看待并發問題時,我們往往認為人類的最基本溝通計算機也可以做到,其實不然,還是那句話,編寫并發程序,要站在計算機的角度來看待問題

粗粒度鎖我們不提倡,所以會使用細粒度鎖,但使用細粒度鎖的時候,我們要嚴格按照 Coffman 的四大條件來逐條判斷,這樣再應用我們這幾個解決方案來解決就好了

靈魂追問

  1.  破壞請求和保持條件時,處理能力的瓶頸在賬本管理員那里,那你覺得這種處理方式會提高并發量嗎?
  2.  破壞請求保持條件的方法和破壞環路等待的方法,你覺得那種方式更好
  3.  破壞請求和保持條件時,如果代碼換成下面的樣子會發生什么? 
  1. public void transfer(Account target, int amt){  
  2.     // 一次性申請轉出賬戶和轉入賬戶,直到成功  
  3.     while(accountBookManager.getAllRequiredAccountBook(this, target)){}  
  4.         try{  
  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.         } finally {  
  16.             accountBookManager.releaseObtainedAccountBook(this, target);  
  17.         }  
  18.     }  
  19.  

 

責任編輯:龐桂玉 來源: segmentfault
相關推薦

2024-04-02 11:22:01

死鎖Java并發

2010-03-16 18:06:29

Java線程死鎖

2011-03-30 13:42:53

云計算

2010-06-29 17:32:13

SQL Server鎖

2024-11-20 08:00:00

死鎖多線程編程

2018-07-13 16:03:08

游戲蘋果推薦傳送騎士

2010-07-20 08:56:37

SQL Server鎖

2012-06-29 09:27:51

2012-06-28 09:45:58

2016-09-18 15:38:10

CMDB配置

2024-03-06 11:27:32

ChatGPTAI技術

2018-08-06 10:08:31

區塊鏈數字貨幣投資

2022-03-30 14:17:16

加密貨幣騙局投資

2020-07-15 09:44:03

數據中心IT技術

2013-05-22 10:43:51

2024-07-11 11:42:09

2022-04-08 08:00:00

NFT數字資產騙局

2014-10-15 10:01:12

2012-01-13 09:33:22

大數據人才技術

2010-04-13 15:05:03

安騰
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 日韩av一区二区在线观看 | av无遮挡| 久久伊人在 | 五月婷亚洲| 成人毛片一区二区三区 | 午夜精品久久 | 91资源在线观看 | 国产成人精品一区二区三区四区 | 第一福利社区1024 | 在线视频第一页 | 日韩免费视频一区二区 | 国产精品久久毛片av大全日韩 | 成人中文字幕在线观看 | 欧美日一区二区 | 亚洲精品成人网 | av免费网| 午夜精品一区二区三区在线视 | 成人欧美一区二区三区黑人孕妇 | 波多野结衣av中文字幕 | 成人伊人网 | 中文字幕高清一区 | 亚洲欧美日韩在线一区二区 | 一级片免费视频 | 最新国产福利在线 | 久草新在线 | 人人人人干 | 五月综合久久 | 成人伊人网 | 欧美一卡二卡在线观看 | 久久久久久免费毛片精品 | www.99热| 久久综合久色欧美综合狠狠 | 狠狠热视频 | 91色视频在线观看 | 7799精品视频天天看 | 欧美日韩在线一区二区 | 国产视频线观看永久免费 | 色综合国产 | 日韩精品视频中文字幕 | 国产成人精品综合 | 精品无码三级在线观看视频 |