Java中死鎖的定位與修復(fù)

我們都知道死鎖是由于多個對象或多個線程之間相互需要 對方鎖持有的鎖而又沒有釋放對方所持有的鎖,導(dǎo)致雙方都永久處于阻塞狀態(tài) ;

小編推薦一個學(xué)JAVA的學(xué)習(xí)裙【四九二,一七三,八四二】,無論你是牛還是小白,是想轉(zhuǎn)行還是想入行都可以來了解一起進步一起學(xué)習(xí)!裙內(nèi)有開發(fā)具,很多干貨和技術(shù)資料分享

如上圖所示,線程1持有對象1的鎖、線程2持有對象2的鎖,持此線程1又想去獲取對象2對象鎖、線程2想獲取對象1對象鎖,此時由于雙方都沒有獲取到想要的鎖,任務(wù)沒完成所以也沒釋放鎖,導(dǎo)致一直僵持呢,于是阻塞、產(chǎn)生死鎖;

死鎖檢測

需要檢測死鎖肯定要先有死鎖出現(xiàn),下面的demo模擬了一個死鎖的產(chǎn)生;

public class DeadlockDemo extends Thread {private BaseObj first;private BaseObj second;public DeadlockDemo(String name, BaseObj first, BaseObj second) { super(name); this.first = first; this.second = second;}public void reentrantLock() throws InterruptedException { first.lock(); System.out.println(String.format("%s 持有:%s 對象鎖,等待獲取:%s對象鎖", this.getName(), first, second)); second.lock(); first.unlock(); second.unlock();}@Overridepublic void run() { try { reentrantLock(); } catch (Exception e) { e.printStackTrace(); }}public static void main(String[] args) throws InterruptedException { ObjOne one = new ObjOne(); ObjTwo two = new ObjTwo(); DeadlockDemo thread1 = new DeadlockDemo("Thread1", one, two); DeadlockDemo thread2 = new DeadlockDemo("Thread2", two, one); thread1.start(); thread2.start(); thread1.join(); thread2.join();} }

運行上面的demo將看到程序被阻塞了,沒法結(jié)束運行;只看到如下運行結(jié)果:

Thread1 持有:objOne 對象鎖,等待獲取:objTwo對象鎖 Thread2 持有:objTwo 對象鎖,等待獲取:objOne對象鎖

小編推薦一個學(xué)JAVA的學(xué)習(xí)裙【四九二,一七三,八四二】,無論你是牛還是小白,是想轉(zhuǎn)行還是想入行都可以來了解一起進步一起學(xué)習(xí)!裙內(nèi)有開發(fā)具,很多干貨和技術(shù)資料分享

這demo沒法結(jié)束運行就是由于產(chǎn)生了死鎖,兩個線程都在相互對待獲取對方所持有的對象鎖;

這時候要解決問題就需要找出哪里出現(xiàn)了死鎖,?通過代碼走查通常不容易發(fā)現(xiàn)死鎖?,當(dāng)然我們這程序很容易發(fā)現(xiàn),因為我們刻意產(chǎn)生的死鎖;所以就需要工具來檢測死鎖,這里可用的工具主要有:jconsole、jvisualvm、jstack等,這些工具其實都是jdk自帶的,用法都很類似;

這里使用jvisualvm來檢測當(dāng)前的demo程序是否產(chǎn)生了死鎖;打開jvisualvm連接到當(dāng)前的應(yīng)用程序即可看到程序的監(jiān)控信息,如內(nèi)存、CPU、性能、GC等等;打開進入線程的tab項查看程序的線程信息,這里很明顯的就看到了提示該程序被檢測除了死鎖!

點擊 線程Dump可以看到線程的堆棧信息,從中可以看到線程的詳細信息,并定位死鎖;

從上圖可以看到線程產(chǎn)生死鎖的原因,Thrad2是等待Thread1、Thread1是等待Thread1, 從下圖的堆棧信息即可定位死鎖產(chǎn)生的位置;

死鎖掃描

除了發(fā)現(xiàn)程序出現(xiàn)問題后我們?nèi)呙杷梨i外,我們還可以實時的去掃描程序用于發(fā)現(xiàn)程序中是否存在死鎖;

JDK提供了MXBean Api可用于掃描程序是否存在死鎖,ThreadMXBean提供了findDeadlockedThreads()方法,可以用于找到產(chǎn)生死鎖的線程;這里在上面的demo程序中添加一個方法用于掃描死鎖,雖然這種方法可以掃描到死鎖但是由于每次都對線程打快照對程序性能會有比較大的影響,所以慎用;

public static void scanDeadLock() { ThreadMXBean mxBean = ManagementFactory.getThreadMXBean(); Runnable runnable = () -> { long[] ids = mxBean.findDeadlockedThreads(); System.out.println("掃描死鎖..."); if (ids != null) { ThreadInfo[] threadInfos = mxBean.getThreadInfo(ids); for (ThreadInfo threadInfo : threadInfos) { System.out.println(threadInfo); } } }; ScheduledExecutorService executorService = new ScheduledThreadPoolExecutor(5, Executors.defaultThreadFactory()); executorService.scheduleAtFixedRate(runnable, 1, 5, TimeUnit.SECONDS);}

避免死鎖

小編推薦一個學(xué)JAVA的學(xué)習(xí)裙【四九二,一七三,八四二】,無論你是牛還是小白,是想轉(zhuǎn)行還是想入行都可以來了解一起進步一起學(xué)習(xí)!裙內(nèi)有開發(fā)具,很多干貨和技術(shù)資料分享

解決死鎖最好的方法就是避免死鎖了,比如上面的demo我們可以把直接使用無參數(shù)的lock()方法換為使用tryLock方法,tryLock還可以指定獲取鎖超時時間,到了超時時間還沒獲得到鎖就會放棄獲取鎖,當(dāng)然還有其它方法可以避免死鎖;

1、避免使用多個鎖、長時間持有鎖;

2、設(shè)計好多個鎖的獲取順序

3、使用帶超時的獲取鎖方法

?著作權(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)容

  • 本文主要講了java中多線程的使用方法、線程同步、線程數(shù)據(jù)傳遞、線程狀態(tài)及相應(yīng)的一些線程函數(shù)用法、概述等。 首先講...
    李欣陽閱讀 2,584評論 1 15
  • Java多線程學(xué)習(xí) [-] 一擴展javalangThread類 二實現(xiàn)javalangRunnable接口 三T...
    影馳閱讀 3,105評論 1 18
  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語法,類相關(guān)的語法,內(nèi)部類的語法,繼承相關(guān)的語法,異常的語法,線程的語...
    子非魚_t_閱讀 34,623評論 18 399
  • 相遇 只是一場意外 相聚 又有太多意外 相離 亦因一場意外 再相逢 猶恐出現(xiàn)意外 總之 意外
    RHIGHT閱讀 295評論 0 0
  • 學(xué)會不斷地清理自己的內(nèi)心 ,才能干凈地在世間行走。心里的垃圾常常會阻塞我們對外界的認(rèn)知, 對自我的認(rèn)知,常常會讓我...
    晨灃閱讀 754評論 2 3

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