java定位死鎖的三種方法(jstack、Arthas和Jvisualvm)
死鎖
死鎖:是指兩個(gè)或兩個(gè)以上的進(jìn)程在執(zhí)行過程中,因爭奪資源而造成的一種互相等待的現(xiàn)象,若無外力作用,它們都將無法推進(jìn)下去。
死鎖發(fā)生的原因
死鎖的發(fā)生是由于資源競爭導(dǎo)致的,導(dǎo)致死鎖的原因如下:
- 系統(tǒng)資源不足,如果系統(tǒng)資源充足,死鎖出現(xiàn)的可能性就很低。
- 進(jìn)程(線程)運(yùn)行推進(jìn)的順序不合適。
- 資源分配不當(dāng)?shù)取?br />
死鎖發(fā)生的條件
死鎖的發(fā)生的四個(gè)必要條件:
- 互斥條件:一個(gè)資源每次只能被一個(gè)進(jìn)程使用。
- 占有且等待:一個(gè)進(jìn)程因請求資源而阻塞時(shí),對已獲得的資源保持不放。
- 不可強(qiáng)行占有:進(jìn)程(線程)已獲得的資源,在未使用完之前,不能強(qiáng)行剝奪。
- 循環(huán)等待條件:若干進(jìn)程(線程)之間形成一種頭尾相接的循環(huán)等待資源關(guān)系。
這四個(gè)條件是死鎖的必要條件,只要系統(tǒng)發(fā)生死鎖,這些條件必然成立,而只要上述條件之一不滿足,就不會發(fā)生死鎖。
1:通過jstack定位死鎖信息
1.1:編寫死鎖代碼
Lock lock1 = new ReentrantLock();
Lock lock2 = new ReentrantLock();
ExecutorService exectuorService = Executors.newFixedThreadPool(2);
exectuorService.submit(() -> {
lock1.lock();
try{
Thread.sleep(1000);
}catch(Exception e){}
try{}
finally{
lock1.unlock();
lock2.unlock();
}
});
exectuorService.submit(() -> {
lock2.lock();
try{
Thread.sleep(1000);
}catch(Exception e){}
try{}
finally{
lock1.unlock();
lock2.unlock();
}
});
1.2:查看死鎖線程的pid
- jps查看死鎖的線程pid
- 使用 jstack -l pid 查看死鎖信息
- 通過打印信息我們可以找到發(fā)生死鎖的代碼是在哪個(gè)位置
"DestroyJavaVM" #13 prio=5 os_prio=31 tid=0x00007f9a1d8fe800 nid=0xd03 waiting on condition [0x0000000000000000] java.lang.Thread.State: RUNNABLE Locked ownable synchronizers: - None "pool-1-thread-2" #12 prio=5 os_prio=31 tid=0x00007f9a1d8fe000 nid=0xa703 waiting on condition [0x000070000ff8e000] java.lang.Thread.State: WAITING (parking) at sun.misc.Unsafe.park(Native Method) - parking to wait for <0x0000000795768cd8> (a java.util.concurrent.locks.ReentrantLock$NonfairSync) at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175) at java.util.concurrent.locks.AbstractQueuedSynchronizer.parkAndCheckInterrupt(AbstractQueuedSynchronizer.java:836) at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireQueued(AbstractQueuedSynchronizer.java:870) at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquire(AbstractQueuedSynchronizer.java:1199) at java.util.concurrent.locks.ReentrantLock$NonfairSync.lock(ReentrantLock.java:209) at java.util.concurrent.locks.ReentrantLock.lock(ReentrantLock.java:285) at com.coco.util.SlideTimeUnit.lambda$main$1(SlideTimeUnit.java:63) at com.coco.util.SlideTimeUnit$$Lambda$2/565760380.run(Unknown Source) at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511) at java.util.concurrent.FutureTask.run(FutureTask.java:266) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) at java.lang.Thread.run(Thread.java:748) Locked ownable synchronizers: - <0x0000000795768d08> (a java.util.concurrent.locks.ReentrantLock$NonfairSync) - <0x0000000795a9e4e0> (a java.util.concurrent.ThreadPoolExecutor$Worker) "pool-1-thread-1" #11 prio=5 os_prio=31 tid=0x00007f9a2082c800 nid=0xa803 waiting on condition [0x000070000fe8b000] java.lang.Thread.State: WAITING (parking) at sun.misc.Unsafe.park(Native Method) - parking to wait for <0x0000000795768d08> (a java.util.concurrent.locks.ReentrantLock$NonfairSync) at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175) at java.util.concurrent.locks.AbstractQueuedSynchronizer.parkAndCheckInterrupt(AbstractQueuedSynchronizer.java:836) at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireQueued(AbstractQueuedSynchronizer.java:870) at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquire(AbstractQueuedSynchronizer.java:1199) at java.util.concurrent.locks.ReentrantLock$NonfairSync.lock(ReentrantLock.java:209) at java.util.concurrent.locks.ReentrantLock.lock(ReentrantLock.java:285) at com.coco.util.SlideTimeUnit.lambda$main$0(SlideTimeUnit.java:49) at com.coco.util.SlideTimeUnit$$Lambda$1/596512129.run(Unknown Source) at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511) at java.util.concurrent.FutureTask.run(FutureTask.java:266) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) at java.lang.Thread.run(Thread.java:748) Locked ownable synchronizers: - <0x0000000795768cd8> (a java.util.concurrent.locks.ReentrantLock$NonfairSync) - <0x0000000795a9ba28> (a java.util.concurrent.ThreadPoolExecutor$Worker) "Service Thread" #10 daemon prio=9 os_prio=31 tid=0x00007f9a2082c000 nid=0x4103 runnable [0x0000000000000000] java.lang.Thread.State: RUNNABLE Locked ownable synchronizers: - None "C1 CompilerThread3" #9 daemon prio=9 os_prio=31 tid=0x00007f9a1e021800 nid=0x3f03 waiting on condition [0x0000000000000000] java.lang.Thread.State: RUNNABLE
2:通過Arthas工具定位死鎖
2.1: 下載好Arthas的jar,然后運(yùn)行
有一個(gè) thread -b 就可以查看到死鎖信息
[arthas@4182]$ thread -b "pool-1-thread-2" Id=12 WAITING on java.util.concurrent.locks.ReentrantLock$NonfairSync@2cb8a9a3 owned by "pool-1-thread-1" Id=11 at sun.misc.Unsafe.park(Native Method) - waiting on java.util.concurrent.locks.ReentrantLock$NonfairSync@2cb8a9a3 at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175) at java.util.concurrent.locks.AbstractQueuedSynchronizer.parkAndCheckInterrupt(AbstractQueuedSynchronizer.java:836) at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireQueued(AbstractQueuedSynchronizer.java:870) at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquire(AbstractQueuedSynchronizer.java:1199) at java.util.concurrent.locks.ReentrantLock$NonfairSync.lock(ReentrantLock.java:209) at java.util.concurrent.locks.ReentrantLock.lock(ReentrantLock.java:285) at com.coco.util.SlideTimeUnit.lambda$main$1(SlideTimeUnit.java:63) at com.coco.util.SlideTimeUnit$$Lambda$2/565760380.run(Unknown Source) at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511) at java.util.concurrent.FutureTask.run(FutureTask.java:266) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) at java.lang.Thread.run(Thread.java:748) Number of locked synchronizers = 2 - java.util.concurrent.ThreadPoolExecutor$Worker@6433a2 - java.util.concurrent.locks.ReentrantLock$NonfairSync@3a855d13 <---- but blocks 1 other threads!
3. 通過 Jvisualvm 定位死鎖
Jvisualvm 是一種自帶的可視化工具,往往在在本地執(zhí)行。
通過 Jvisualvm 命令打開軟件,選中進(jìn)程,進(jìn)入線程視圖,會給出死鎖提示:

死鎖的預(yù)防
- 盡量避免使用多個(gè)鎖,并且只有需要時(shí)才持有鎖。
- 如果使用多個(gè)鎖,一定要設(shè)計(jì)好鎖的獲取順序。
- 使用帶有超時(shí)的方法,為程序帶來更多的可控性,比如指定獲取鎖的時(shí)間最多為5秒,超時(shí)就放棄。
- 通過一些代碼靜態(tài)檢查工具發(fā)現(xiàn)可能存在的死鎖問題,比如FindBugs。
總結(jié)
到此這篇關(guān)于java定位死鎖的文章就介紹到這了,更多相關(guān)java定位死鎖內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
服務(wù)器實(shí)現(xiàn)Java遠(yuǎn)程訪問Linux服務(wù)器方式(JSch)
文章介紹了如何使用Java遠(yuǎn)程訪問Linux服務(wù)器,主要包括建立SSH連接、使用JSch庫執(zhí)行命令、解析返回值以及關(guān)閉連接的步驟2024-11-11
Springboot項(xiàng)目啟動時(shí)如何用命令動態(tài)指定環(huán)境
這篇文章主要介紹了Springboot項(xiàng)目啟動時(shí)如何用命令動態(tài)指定環(huán)境的操作,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-06-06
java虛擬機(jī)內(nèi)存溢出及泄漏實(shí)例
本篇文章給大家分享了java虛擬機(jī)內(nèi)存溢出及泄漏的實(shí)例以及相關(guān)知識點(diǎn)分享,有興趣的朋友參考學(xué)習(xí)下。2018-06-06

