java并發(fā)--java死鎖

本篇結(jié)構(gòu):

  • 前言
  • 什么是死鎖
  • 產(chǎn)生死鎖的必要條件
  • 死鎖的代碼示例
  • 死鎖排查
  • 如何避免死鎖
  • 總結(jié)

一、前言

今天被問到什么是死鎖,腦袋一抽,半天沒想起來,想來就是不夠熟悉了,就趁這個點兒記錄一下,也順便了解下分析死鎖的方法,之前這塊的經(jīng)驗的確不算多。

二、什么是死鎖

那什么是死鎖呢?

百度百科的解釋:死鎖是指兩個或兩個以上的進(jìn)程在執(zhí)行過程中,由于競爭資源或者由于彼此通信而造成的一種阻塞的現(xiàn)象,若無外力作用,它們都將無法推進(jìn)下去。

舉個例子:生活中經(jīng)常能看到很窄的巷子只能允許一輛車通過,但是有時就會出現(xiàn)兩輛車迎頭遇上的情況,這時候甲車等乙車讓出路,乙車又等甲車讓出路,甲車乙車互不相讓,造成的結(jié)果就是兩輛車卡在巷子里,都不能動了。

這個例子反映在程序中就是死鎖。

線程一持有鎖A,接著想獲取鎖B,而線程二持有鎖B,接著想獲取鎖A,結(jié)果就是線程一等不到鎖B,線程二等不到鎖A,造成死鎖。

三、產(chǎn)生死鎖的必要條件

  1. 互斥條件:就是一個資源同時只能有一個進(jìn)程占有,不能有兩個或是兩個以上的占有。
  2. 不可搶占條件:在一個進(jìn)程所獲取的資源在未使用完畢之前,資源申請者不能強行的從資源占有者手中搶奪資源。
  3. 占有申請條件:進(jìn)程已經(jīng)占有了一個資源,此時又申請新的資源;但這個新申請的資源已經(jīng)被別的進(jìn)程占有了,該進(jìn)程就會阻塞,在獲取申請的資源之前他還會一直占有已占有的那個資源。
  4. 循環(huán)等待條件:存在一個循環(huán)等待序列,p1等待p2,p2等待p3,p3等待p1。形成一個進(jìn)程循環(huán)等待。

上述四個條件在死鎖是會同時發(fā)送,也就是只要一個必要條件不通過,則就不會產(chǎn)生死鎖。

四、死鎖的代碼示例

public class TestDeadLock {
    private static Lock lockA = new ReentrantLock();
    private static Lock lockB = new ReentrantLock();

    public static void main(String args[]) {
        new Thread(() -> {
            try {
                lockA.lock();
                TimeUnit.SECONDS.sleep(2);
                try {
                    lockB.lock();
                } finally {
                    lockB.unlock();
                }
            } catch (InterruptedException e) {

            } finally {
                lockA.unlock();
            }
        }).start();

        new Thread(() -> {
            try {
                lockB.lock();
                TimeUnit.SECONDS.sleep(2);
                try {
                    lockA.lock();
                } finally {
                    lockA.unlock();
                }
            } catch (InterruptedException e) {

            } finally {
                lockB.unlock();
            }
        }).start();
    }
}

如上,是一個死鎖的例子,運行代碼會發(fā)現(xiàn)程序被阻塞,如果不進(jìn)行人為干預(yù),程序一直卡住。

相信看過上面代碼都能很快找到問題,一個線程先持有了lockA,然后請求lockB,而另一個線程先持有了lockB,然后請求lockA,結(jié)果就是線程一等不到lockB(已經(jīng)被線程二持有),線程二等不到lockA(已經(jīng)被線程一持有),相互等待造成死鎖。

五、死鎖排查

這是我們主動創(chuàng)造的死鎖,很容易定位,但現(xiàn)實代碼中往往是沒這么直觀的,該怎么分析呢?

可以利用jdk的bin目錄下的jvisualvm.exe工具,雙擊運行可以看到如下的圖形界面:

左邊是JVM進(jìn)程列表,找到對應(yīng)的進(jìn)程雙擊查看,切換到線程欄目,可以看到右側(cè)的信息,也特意截了個圖:

可以看到正上方有檢測到死鎖的紅字提示,接著看線程的狀況,也可以發(fā)現(xiàn)是駐留的狀態(tài)。

已經(jīng)知道出現(xiàn)了死鎖,就要確定哪里出現(xiàn)了死鎖,可以點擊右上方的線程Dump按鈕,dump出可以找到一些關(guān)鍵信息:

大致是說:線程1在等待線程0持有的資源,線程0在等待線程1持有的資源。

接著看可以找到哪里出現(xiàn)死鎖:

說36行和20行出現(xiàn)了死鎖。

六、如何避免死鎖

日常開發(fā)應(yīng)該盡量避免死鎖的產(chǎn)生。

  1. 盡量不要寫順序不一致加鎖的代碼
  2. 如果必須要寫順序不一致的加鎖的代碼,可以采用trylock 方法,避免程序一直等待
  3. 預(yù)防死鎖可以采用finddebug插件去掃描代碼發(fā)現(xiàn)死鎖

如果出現(xiàn)了死鎖,大約只能停止程序,代價非常大,所以要盡量避免死鎖。

七、總結(jié)

上面大約都是紙上談兵,各位做個參考即可,能有點幫助是最好了。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容