Java多線程之死鎖的出現和解決方法

Java多線程之死鎖的出現和解決方法

北大青鳥長沙麓谷校區(qū)      2022-04-25 01:00:01     1

Java多線程之死鎖的出現和解決方法,   什么是死鎖?  死鎖是這樣一種情形:多個線程同時被阻塞,它們中的一個或者全部都在等待某個資源被釋放.由于線程被無限期地

課程價格 請咨詢

上課時段: 授課校區(qū):

詳細介紹

 


    什么是死鎖?


  死鎖是這樣一種情形:多個線程同時被阻塞,它們中的一個或者全部都在等待某個資源被釋放.由于線程被無限期地阻塞,因此程序不能正常運行.形象的說就是:一個寶藏需要兩把鑰匙來打開,同時間正好來了兩個人,他們一人一把鑰匙,但是雙方都再等著對方能交出鑰匙來打開寶藏,誰都沒釋放自己的那把鑰匙.就這樣這倆人一直僵持下去,直到開發(fā)人員發(fā)現這個局面。


  導致死鎖的根源在于不適當地運用“synchronized”關鍵詞來管理線程對特定對象的訪問.“synchronized”關鍵詞的作用是,確保在某個時刻只有一個線程被允許執(zhí)行特定的代碼塊,因此,被允許執(zhí)行的線程首先必須擁有對變量或對象的排他性訪問權.當線程訪問對象時,線程會給對象加鎖,而這個鎖導致其它也想訪問同一對象的線程被阻塞,直至第一個線程釋放它加在對象上的鎖。


  舉個例子


  死鎖的產生大部分都是在你不知情的時候.我們通過一個例子來看下什么是死鎖。


  1、synchronized嵌套

  synchronized關鍵字可以保證多線程再訪問到synchronized修飾的方法的時候保證了同步性.就是線程A訪問到這個方法的時候線程B同時也來訪問這個方法,這時線程B將進行阻塞,等待線程A執(zhí)行完才可以去訪問.這里就要用到synchronized所持有的同步鎖.具體來看代碼:


/首先我們先定義兩個final的對象鎖.可以看做是共有的資源.

 final Object lockA = new Object();

 final Object lockB = new Object();

//生產者A

 class ProductThreadA implements Runnable{

   @Override

   public void run() {

//這里一定要讓線程睡一會兒來模擬處理數據 ,要不然的話死鎖的現象不會那么的明顯.這里就是同步語句塊里面,首先獲得對象鎖lockA,然后執(zhí)行一些代碼,隨后我們需要對象鎖lockB去執(zhí)行另外一些代碼.

     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開始執(zhí)行run方法的時候,它會先持有對象鎖localA,然后睡眠2秒,這時候threadB也開始執(zhí)行run方法,它持有的是localB對象鎖.當threadA運行到第二個同步方法的時候,發(fā)現localB的對象鎖不能使用(threadB未釋放localB鎖),threadA就停在這里等待localB鎖.隨后threadB也執(zhí)行到第二個同步方法,去訪localA對象鎖的時候發(fā)現localA還沒有被釋放(threadA未釋放localA鎖),threadB也停在這里等待localA鎖釋放.就這樣兩個線程都沒辦法繼續(xù)執(zhí)行下去,進入死鎖的狀態(tài). 看下運行結果:


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的,這里明顯的出現了死鎖的現象。


  死鎖出現的原因


  當我們了解在什么情況下會產生死鎖,以及什么是死鎖的時候,我們在寫代碼的時候應該盡量的去避免這個誤區(qū).產生死鎖必須同時滿足以下四個條件,只要其中任一條件不成立,死鎖就不會發(fā)生。


  互斥條件:線程要求對所分配的資源進行排他性控制,即在一段時間內某 資源僅為一個進程所占有.此時若有其他進程請求該資源.則請求進程只能等待。


  不剝奪條件:進程所獲得的資源在未使用完畢之前,不能被其他進程強行奪走,即只能由獲得該資源的線程自己來釋放(只能是主動釋放)。


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


  循環(huán)等待條件:存在一種線程資源的循環(huán)等待鏈,鏈中每一個線程已獲得的資源同時被鏈中下一個線程所請求。


  解決死鎖的方法


  1、解決死鎖主要方法如下:


 ?。?)不考慮此問題,樂觀的角度,鴕鳥算法


 ?。?)不讓死鎖發(fā)生:


  ①死鎖預防。


  靜態(tài)策略,通過設計合適的資源分配算法,不讓死鎖發(fā)生


 ?、谒梨i避免


  動態(tài)策略,以不讓死鎖發(fā)生為目標,跟蹤并評估資源分配過程,根據評估結果決策是否分配


  (3)讓死鎖發(fā)生:死鎖的檢測與解除


  2.死鎖預防的具體方法:主要是破壞產生死鎖的四個必要條件中的任何一個條件。


  (1)破壞“互斥使用/資源獨占”條件


  使用資源轉換技術,將獨占資源變?yōu)楣蚕碣Y源


  (2)破壞“占有且等待”條件


 ?、俜桨?:每個進程在運行前必須一次性的申請它所要求的全部資源,且僅當該進程所要的資源均可滿足時才一次性的分配。


  資源利用率低,“饑餓”現象


 ?、谠谠试S進程動態(tài)申請資源的前提下規(guī)定,一個進行在申請新資源,且不能立即得到滿足,必須釋放已占有的全部資源。若需要再重新申請


  (3)破壞“不可搶占”條件


  可以通過操作系統(tǒng)搶占這一資源(根據進程的不同優(yōu)先級)


  局限性:適用于狀態(tài)易于保存和恢復的資源,如CPU(搶占式的調度算法),內存(頁面置換算法)


  (4)破壞“循環(huán)等待”條件


  通過定義資源類型的線性順序實現


  方案:資源有序分配法。(如哲學家就餐問題)


  也就是把資源中所有的資源編號,進程在申請資源時,必須嚴格按照資源編號的遞增次序進行,否則操作系統(tǒng)不予分配。(資源使用的頻繁性?)


  3、死鎖避免的方法


  

 ?。?)對不同分區(qū)進行不同的處理,左下可同時分配資源;右下區(qū)域可能會產生死鎖,應該先分配P,釋放后再分配Q;左上同理;右上區(qū)域時會產生死鎖現象,不予分配資源。對可能發(fā)生死鎖和可能產生死鎖的不予分配資源,否則(安全狀態(tài))分配資源。


  (2)安全狀態(tài)是指每個進程Pi以后還需要的資源量不超過系統(tǒng)當前剩余資源量與所有進程Pj當前占有資源量之和。


  4、死鎖的檢測與解除

  (1)允許死鎖發(fā)生,但是操作系統(tǒng)會不斷監(jiān)視系統(tǒng)進展情況,判斷死鎖是否真的發(fā)生。(進程等待時檢測,定時檢測,系統(tǒng)資源利用率下降)


  (2)一旦死鎖發(fā)生,采用專門的措施,解除死鎖并以最小的代價恢復系統(tǒng)運行。


  以上就是北大青鳥長沙麓谷校區(qū)java學院小編介紹的“Java多線程之死鎖的出現和解決方法”的相關內容,希望對大家有幫助,更多精彩內容請關注北大青鳥長沙麓谷校區(qū)java學院官網。



培訓啦提醒您:交易時請核實對方資質,對于過大宣傳或承諾需謹慎!任何要求預付定金、匯款等方式均存在風險,謹防上當。