Java多執行緒-死鎖
什麼是死鎖?
死鎖是這樣一種情形:多個執行緒同時被阻塞,它們中的一個或者全部都在等待某個資源被釋放.由於執行緒被無限期地阻塞,因此程式不能正常執行.形象的說就是:一個寶藏需要兩把鑰匙來開啟,同時間正好來了兩個人,他們一人一把鑰匙,但是雙方都再等著對方能交出鑰匙來開啟寶藏,誰都沒釋放自己的那把鑰匙.就這樣這倆人一直僵持下去,直到開發人員發現這個局面.
導致死鎖的根源在於不適當地運用“synchronized”關鍵詞來管理執行緒對特定物件的訪問.“synchronized”關鍵詞的作用是,確保在某個時刻只有一個執行緒被允許執行特定的程式碼塊,因此,被允許執行的執行緒首先必須擁有對變數或物件的排他性訪問權.當執行緒訪問物件時,執行緒會給物件加鎖,而這個鎖導致其它也想訪問同一物件的執行緒被阻塞,直至第一個執行緒釋放它加在物件上的鎖.
對synchronized不太瞭解的話請點選這裡
舉個例子
死鎖的產生大部分都是在你不知情的時候.我們通過一個例子來看下什麼是死鎖.
1.synchronized巢狀.
synchronized關鍵字可以保證多執行緒再訪問到synchronized修飾的方法的時候保證了同步性.就是執行緒A訪問到這個方法的時候執行緒B同時也來訪問這個方法,這時執行緒B將進行阻塞,等待執行緒A執行完才可以去訪問.這裡就要用到synchronized所持有的同步鎖.具體來看程式碼:
//首先我們先定義兩個final的物件鎖.可以看做是共有的資源. final Object lockA = new Object(); final Object lockB = new Object(); //生產者A class ProductThreadA implements Runnable{ @Override public void run() { //這裡一定要讓執行緒睡一會兒來模擬處理資料 ,要不然的話死鎖的現象不會那麼的明顯.這裡就是同步語句塊裡面,首先獲得物件鎖lockA,然後執行一些程式碼,隨後我們需要物件鎖lockB去執行另外一些程式碼. synchronized (lockA){ //這裡一個log日誌 Log.e("CHAO","ThreadA lock lockA"); try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } synchronized (lockB){ //這裡一個log日誌 Log.e("CHAO","ThreadA lock lockB"); try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } } } } } //生產者B class ProductThreadB implements Runnable{ //我們生產的順序真好好生產者A相反,我們首先需要物件鎖lockB,然後需要物件鎖lockA. @Override public void run() { synchronized (lockB){ //這裡一個log日誌 Log.e("CHAO","ThreadB lock lockB"); try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } synchronized (lockA){ //這裡一個log日誌 Log.e("CHAO","ThreadB lock lockA"); try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } } } } } //這裡執行執行緒 ProductThreadA productThreadA = new ProductThreadA(); ProductThreadB productThreadB = new ProductThreadB(); Thread threadA = new Thread(productThreadA); Thread threadB = new Thread(productThreadB); threadA.start(); threadB.start();複製程式碼
分析一下,當threadA開始執行run方法的時候,它會先持有物件鎖localA,然後睡眠2秒,這時候threadB也開始執行run方法,它持有的是localB物件鎖.當threadA執行到第二個同步方法的時候,發現localB的物件鎖不能使用(threadB未釋放localB鎖),threadA就停在這裡等待localB鎖.隨後threadB也執行到第二個同步方法,去訪問localA物件鎖的時候發現localA還沒有被釋放(threadA未釋放localA鎖),threadB也停在這裡等待localA鎖釋放.就這樣兩個執行緒都沒辦法繼續執行下去,進入死鎖的狀態. 看下執行結果:
10-20 14:54:39.940 18162-18178/? E/CHAO: ThreadA lock lockA 10-20 14:54:39.940 18162-18179/? E/CHAO: ThreadB lock lockB複製程式碼
當不會死鎖的時候應該是列印四條log的,這裡明顯的出現了死鎖的現象.
死鎖出現的原因
當我們瞭解在什麼情況下會產生死鎖,以及什麼是死鎖的時候,我們在寫程式碼的時候應該儘量的去避免這個誤區.產生死鎖必須同時滿足以下四個條件,只要其中任一條件不成立,死鎖就不會發生.
• 互斥條件:執行緒要求對所分配的資源進行排他性控制,即在一段時間內某 資源僅為一個程式所佔有.此時若有其他程式請求該資源.則請求程式只能等待.
• 不剝奪條件:程式所獲得的資源在未使用完畢之前,不能被其他程式強行奪走,即只能由獲得該資源的執行緒自己來釋放(只能是主動釋放).
• 請求和保持條件:執行緒已經保持了至少一個資源,但又提出了新的資源請求,而該資源已被其他執行緒佔有,此時請求執行緒被阻塞,但對自己已獲得的資源保持不放.
• 迴圈等待條件:存在一種執行緒資源的迴圈等待鏈,鏈中每一個執行緒已獲得的資源同時被鏈中下一個執行緒所請求。
死鎖的解決方法
說實話避免死鎖還得再自己寫程式碼的時候注意一下.這裡引用別人的解決方法,不過我對於這些解決方法不是太懂,講的太含糊沒有具體的例項.
原始碼地址 github.com/WangcWj/Thr…
歡迎各位吐槽,給個贊吧~