我們都知道死鎖是由于多個對象或多個線程之間相互需要 對方鎖持有的鎖而又沒有釋放對方所持有的鎖,導(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、使用帶超時的獲取鎖方法