欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

詳解java中產(chǎn)生死鎖的原因及如何避免

 更新時(shí)間:2019年04月04日 15:30:05   作者:yehao_123456  
這篇文章主要介紹了java中產(chǎn)生死鎖的原因及如何避免,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧

1. Java中導(dǎo)致死鎖的原因

Java中死鎖最簡(jiǎn)單的情況是,一個(gè)線(xiàn)程T1持有鎖L1并且申請(qǐng)獲得鎖L2,而另一個(gè)線(xiàn)程T2持有鎖L2并且申請(qǐng)獲得鎖L1,因?yàn)槟J(rèn)的鎖申請(qǐng)操作都是阻塞的,所以線(xiàn)程T1和T2永遠(yuǎn)被阻塞了。導(dǎo)致了死鎖。這是最容易理解也是最簡(jiǎn)單的死鎖的形式。但是實(shí)際環(huán)境中的死鎖往往比這個(gè)復(fù)雜的多??赡軙?huì)有多個(gè)線(xiàn)程形成了一個(gè)死鎖的環(huán)路,比如:線(xiàn)程T1持有鎖L1并且申請(qǐng)獲得鎖L2,而線(xiàn)程T2持有鎖L2并且申請(qǐng)獲得鎖L3,而線(xiàn)程T3持有鎖L3并且申請(qǐng)獲得鎖L1,這樣導(dǎo)致了一個(gè)鎖依賴(lài)的環(huán)路:T1依賴(lài)T2的鎖L2,T2依賴(lài)T3的鎖L3,而T3依賴(lài)T1的鎖L1。從而導(dǎo)致了死鎖。

從這兩個(gè)例子,我們可以得出結(jié)論,產(chǎn)生死鎖可能性的最根本原因是:線(xiàn)程在獲得一個(gè)鎖L1的情況下再去申請(qǐng)另外一個(gè)鎖L2,也就是鎖L1想要包含了鎖L2,也就是說(shuō)在獲得了鎖L1,并且沒(méi)有釋放鎖L1的情況下,又去申請(qǐng)獲得鎖L2,這個(gè)是產(chǎn)生死鎖的最根本原因。另一個(gè)原因是默認(rèn)的鎖申請(qǐng)操作是阻塞的。

2. Java中如何避免死鎖

既然我們知道了產(chǎn)生死鎖可能性的原因,那么就可以在編碼時(shí)進(jìn)行規(guī)避。Java是面向?qū)ο蟮木幊陶Z(yǔ)言,程序的最小單元是對(duì)象,對(duì)象封裝了數(shù)據(jù)和操作,所以Java中的鎖一般也是以對(duì)象為單位的,對(duì)象的內(nèi)置鎖保護(hù)對(duì)象中的數(shù)據(jù)的并發(fā)訪(fǎng)問(wèn)。所以如果我們能夠避免在對(duì)象的同步方法中調(diào)用其它對(duì)象的同步方法,那么就可以避免死鎖產(chǎn)生的可能性。如下所示的代碼,就存在死鎖的可能性:

public class ClassB {
  private String address;
  // ...
  
  public synchronized void method1(){
    // do something
  }
  // ... ...
}
public class ClassA {
  private int id;
  private String name;
  private ClassB b;
  // ...
  
  public synchronized void m1(){
    // do something
    b.method1();
  }
  // ... ...
}

上面的ClassA.m1()方法,在對(duì)象的同步方法中又調(diào)用了ClassB的同步方法method1(),所以存在死鎖發(fā)生的可能性。我們可以修改如下,避免死鎖:

public class ClassA {
  private int id;
  private String name;
  private ClassB b;
  // ...
  
  public void m2(){
    synchronized(this){
      // do something
    }
    b.method1();
  }
  // ... ...
}

這樣的話(huà)減小了鎖定的范圍,兩個(gè)鎖的申請(qǐng)就沒(méi)有發(fā)生交叉,避免了死鎖的可能性,這是最理性的情況,因?yàn)殒i沒(méi)有發(fā)生交叉。但是有時(shí)是不允許我們這樣做的。此時(shí),如果只有ClassA中只有一個(gè)m1這樣的方法,需要同時(shí)獲得兩個(gè)對(duì)象上的鎖,并且不會(huì)將實(shí)例屬性 b 溢出(return b;),而是將實(shí)例屬性 b 封閉在對(duì)象中,那么也不會(huì)發(fā)生死鎖。因?yàn)闊o(wú)法形成死鎖的閉環(huán)。但是如果ClassA中有多個(gè)方法需要同時(shí)獲得兩個(gè)對(duì)象上的鎖,那么這些方法就必須以相同的順序獲得鎖。

比如銀行轉(zhuǎn)賬的場(chǎng)景下,我們必須同時(shí)獲得兩個(gè)賬戶(hù)上的鎖,才能進(jìn)行操作,兩個(gè)鎖的申請(qǐng)必須發(fā)生交叉。這時(shí)我們也可以打破死鎖的那個(gè)閉環(huán),在涉及到要同時(shí)申請(qǐng)兩個(gè)鎖的方法中,總是以相同的順序來(lái)申請(qǐng)鎖,比如總是先申請(qǐng) id 大的賬戶(hù)上的鎖 ,然后再申請(qǐng) id 小的賬戶(hù)上的鎖,這樣就無(wú)法形成導(dǎo)致死鎖的那個(gè)閉環(huán)。

public class Account {
  private int id;  // 主鍵
  private String name;
  private double balance;
  
  public void transfer(Account from, Account to, double money){
    if(from.getId() > to.getId()){
      synchronized(from){
        synchronized(to){
          // transfer
        }
      }
    }else{
      synchronized(to){
        synchronized(from){
          // transfer
        }
      }
    }
  }

  public int getId() {
    return id;
  }
}

這樣的話(huà),即使發(fā)生了兩個(gè)賬戶(hù)比如 id=1的和id=100的兩個(gè)賬戶(hù)相互轉(zhuǎn)賬,因?yàn)椴还苁悄膫€(gè)線(xiàn)程先獲得了id=100上的鎖,另外一個(gè)線(xiàn)程都不會(huì)去獲得id=1上的鎖(因?yàn)樗麤](méi)有獲得id=100上的鎖),只能是哪個(gè)線(xiàn)程先獲得id=100上的鎖,哪個(gè)線(xiàn)程就先進(jìn)行轉(zhuǎn)賬。這里除了使用id之外,如果沒(méi)有類(lèi)似id這樣的屬性可以比較,那么也可以使用對(duì)象的hashCode()的值來(lái)進(jìn)行比較。

上面我們說(shuō)到,死鎖的另一個(gè)原因是默認(rèn)的鎖申請(qǐng)操作是阻塞的,所以如果我們不使用默認(rèn)阻塞的鎖,也是可以避免死鎖的。我們可以使用ReentrantLock.tryLock()方法,在一個(gè)循環(huán)中,如果tryLock()返回失敗,那么就釋放以及獲得的鎖,并睡眠一小段時(shí)間。這樣就打破了死鎖的閉環(huán)。

比如:線(xiàn)程T1持有鎖L1并且申請(qǐng)獲得鎖L2,而線(xiàn)程T2持有鎖L2并且申請(qǐng)獲得鎖L3,而線(xiàn)程T3持有鎖L3并且申請(qǐng)獲得鎖L1

此時(shí)如果T3申請(qǐng)鎖L1失敗,那么T3釋放鎖L3,并進(jìn)行睡眠,那么T2就可以獲得L3了,然后T2執(zhí)行完之后釋放L2, L3,所以T1也可以獲得L2了執(zhí)行完然后釋放鎖L1, L2,然后T3睡眠醒來(lái),也可以獲得L1, L3了。打破了死鎖的閉環(huán)。

這些情況,都還是比較好處理的,因?yàn)樗鼈兌际窍嚓P(guān)的,我們很容易意識(shí)到這里有發(fā)生死鎖的可能性,從而可以加以防備。很多情況的場(chǎng)景都不會(huì)很明顯的讓我們察覺(jué)到會(huì)存在發(fā)生死鎖的可能性。所以我們還是要注意:

一旦我們?cè)谝粋€(gè)同步方法中,或者說(shuō)在一個(gè)鎖的保護(hù)的范圍中,調(diào)用了其它對(duì)象的方法時(shí),就要十而分的小心:

1)如果其它對(duì)象的這個(gè)方法會(huì)消耗比較長(zhǎng)的時(shí)間,那么就會(huì)導(dǎo)致鎖被我們持有了很長(zhǎng)的時(shí)間;

2)如果其它對(duì)象的這個(gè)方法是一個(gè)同步方法,那么就要注意避免發(fā)生死鎖的可能性了;

最好是能夠避免在一個(gè)同步方法中調(diào)用其它對(duì)象的延時(shí)方法和同步方法。如果不能避免,就要采取上面說(shuō)到的編碼技巧,打破死鎖的閉環(huán),防止死鎖的發(fā)生。同時(shí)我們還可以盡量使用“不可變對(duì)象”來(lái)避免鎖的使用,在某些情況下還可以避免對(duì)象的共享,比如 new 一個(gè)新的對(duì)象代替共享的對(duì)象,因?yàn)殒i一般是對(duì)象上的,對(duì)象不相同了,也就可以避免死鎖,另外盡量避免使用靜態(tài)同步方法,因?yàn)殪o態(tài)同步相當(dāng)于全局鎖。還有一些封閉技術(shù)可以使用:比如堆棧封閉,線(xiàn)程封閉,ThreadLocal,這些技術(shù)可以減少對(duì)象的共享,也就減少了死鎖的可能性。

總結(jié)一下:

死鎖的根本原因

1)是多個(gè)線(xiàn)程涉及到多個(gè)鎖,這些鎖存在著交叉,所以可能會(huì)導(dǎo)致了一個(gè)鎖依賴(lài)的閉環(huán);

2)默認(rèn)的鎖申請(qǐng)操作是阻塞的。

所以要避免死鎖,就要在一遇到多個(gè)對(duì)象鎖交叉的情況,就要仔細(xì)審查這幾個(gè)對(duì)象的類(lèi)中的所有方法,是否存在著導(dǎo)致鎖依賴(lài)的環(huán)路的可能性。要采取各種方法來(lái)杜絕這種可能性。

以上所述是小編給大家介紹的java中產(chǎn)生死鎖的原因及如何避免詳解整合,希望對(duì)大家有所幫助,如果大家有任何疑問(wèn)請(qǐng)給我留言,小編會(huì)及時(shí)回復(fù)大家的。在此也非常感謝大家對(duì)腳本之家網(wǎng)站的支持!

相關(guān)文章

  • Java sleep方法及中斷方式、yield方法代碼實(shí)例

    Java sleep方法及中斷方式、yield方法代碼實(shí)例

    這篇文章主要介紹了Java sleep方法及中斷方式、yield方法代碼實(shí)例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-04-04
  • JAVA集合框架Map特性及實(shí)例解析

    JAVA集合框架Map特性及實(shí)例解析

    這篇文章主要介紹了JAVA集合框架Map特性及實(shí)例解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-04-04
  • 詳解RabbitMq如何做到消息的可靠性投遞

    詳解RabbitMq如何做到消息的可靠性投遞

    這篇文章主要為大家介紹了RabbitMq如何做到消息的可靠性投遞,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-09-09
  • MyBatis-Plus 動(dòng)態(tài)表名SQL解析器的實(shí)現(xiàn)

    MyBatis-Plus 動(dòng)態(tài)表名SQL解析器的實(shí)現(xiàn)

    這篇文章主要介紹了MyBatis-Plus 動(dòng)態(tài)表名SQL解析器的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2020-08-08
  • Java中多線(xiàn)程下載圖片并壓縮能提高效率嗎

    Java中多線(xiàn)程下載圖片并壓縮能提高效率嗎

    本文主要介紹了Java中多線(xiàn)程下載圖片并壓縮能提高效率嗎,很多人都想知道這個(gè)問(wèn)題,本文就來(lái)詳細(xì)介紹一下,感興趣的小伙伴們可以參考一下
    2021-07-07
  • MybatisPlus中QueryWrapper常用方法總結(jié)

    MybatisPlus中QueryWrapper常用方法總結(jié)

    MyBatis-Plus是一個(gè)Mybatis增強(qiáng)版工具,在MyBatis上擴(kuò)充了其他功能沒(méi)有改變其基本功能,為了簡(jiǎn)化開(kāi)發(fā)提交效率而存在,queryWrapper是mybatis plus中實(shí)現(xiàn)查詢(xún)的對(duì)象封裝操作類(lèi),本文就給大家總結(jié)了MybatisPlus中QueryWrapper的常用方法,需要的朋友可以參考下
    2023-07-07
  • 自己動(dòng)手寫(xiě)一個(gè)java版簡(jiǎn)單云相冊(cè)

    自己動(dòng)手寫(xiě)一個(gè)java版簡(jiǎn)單云相冊(cè)

    這篇文章主要為大家分享了自己動(dòng)手寫(xiě)的一個(gè)java版簡(jiǎn)單云相冊(cè),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2016-07-07
  • Java基礎(chǔ)之類(lèi)型封裝器示例

    Java基礎(chǔ)之類(lèi)型封裝器示例

    這篇文章主要介紹了Java基礎(chǔ)之類(lèi)型封裝器,結(jié)合實(shí)例形式分析了java類(lèi)型封裝相關(guān)原理與操作技巧,需要的朋友可以參考下
    2019-08-08
  • Java web含驗(yàn)證碼及權(quán)限登錄實(shí)例代碼

    Java web含驗(yàn)證碼及權(quán)限登錄實(shí)例代碼

    這篇文章主要介紹了Java web含驗(yàn)證碼及權(quán)限登錄實(shí)例代碼,所用到的開(kāi)發(fā)工具為myeclipse10,MySQL數(shù)據(jù)庫(kù),具體實(shí)現(xiàn)代碼大家參考下本文吧
    2017-03-03
  • springboot創(chuàng)建多module項(xiàng)目的實(shí)例

    springboot創(chuàng)建多module項(xiàng)目的實(shí)例

    這篇文章主要介紹了springboot創(chuàng)建多module項(xiàng)目的實(shí)例代碼,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-02-02

最新評(píng)論