java定位死鎖的三種方法(jstack、Arthas和Jvisualvm)
死鎖
死鎖:是指兩個或兩個以上的進程在執行過程中,因爭奪資源而造成的一種互相等待的現象,若無外力作用,它們都將無法推進下去。
死鎖發生的原因
死鎖的發生是由於資源競爭導致的,導致死鎖的原因如下:
- 系統資源不足,如果系統資源充足,死鎖出現的可能性就很低。
- 進程(線程)運行推進的順序不合適。
- 資源分配不當等。
死鎖發生的條件
死鎖的發生的四個必要條件:
- 互斥條件:一個資源每次隻能被一個進程使用。
- 占有且等待:一個進程因請求資源而阻塞時,對已獲得的資源保持不放。
- 不可強行占有:進程(線程)已獲得的資源,在未使用完之前,不能強行剝奪。
- 循環等待條件:若幹進程(線程)之間形成一種頭尾相接的循環等待資源關系。
這四個條件是死鎖的必要條件,隻要系統發生死鎖,這些條件必然成立,而隻要上述條件之一不滿足,就不會發生死鎖。
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 查看死鎖信息
- 通過打印信息我們可以找到發生死鎖的代碼是在哪個位置
"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,然後運行
有一個 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 是一種自帶的可視化工具,往往在在本地執行。
通過 Jvisualvm 命令打開軟件,選中進程,進入線程視圖,會給出死鎖提示:
死鎖的預防
- 盡量避免使用多個鎖,並且隻有需要時才持有鎖。
- 如果使用多個鎖,一定要設計好鎖的獲取順序。
- 使用帶有超時的方法,為程序帶來更多的可控性,比如指定獲取鎖的時間最多為5秒,超時就放棄。
- 通過一些代碼靜態檢查工具發現可能存在的死鎖問題,比如FindBugs。
總結
到此這篇關於java定位死鎖的文章就介紹到這瞭,更多相關java定位死鎖內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!
推薦閱讀:
- 淺談Java並發中ReentrantLock鎖應該怎麼用
- 手寫Java LockSupport的示例代碼
- ReentrantLock獲取鎖釋放鎖的流程示例分析
- Java並發編程之ReentrantLock可重入鎖的實例代碼
- 詳解Java中的ReentrantLock鎖