java定位死鎖的三種方法(jstack、Arthas和Jvisualvm)
阿新 • • 發佈:2021-09-07
目錄
- 死鎖
- 死鎖發生的原因
- 死鎖發生的條件
- 1:通過tack定位死鎖資訊
- 1.2:檢視死鎖執行緒的pid
- 2:通過Arthas工具定位死鎖
- 3. 通過 Jvisualvm 定位死鎖
- 死鎖的預防
- 總結
死鎖
死鎖:是指兩個或兩個以上的程序在執行過程中,因爭奪資源而造成的一種互相等待的現象,若無外力作用,它們都將無法推進下去。
死鎖發生的原因
死鎖的發生是由於資源競爭導致的,導致死鎖的原因如下:
- 系統資源不足,如果系統資源充足,死鎖出現的可能性就很低。
- 程序(執行緒)執行推進的順序不合適。
- 資源分配不當等。
死鎖發生的條件
死鎖的發生的四個必要條件:
- 互斥條件:一個資源每次只能被一個程序使用。
- 佔有且等待:一個程序因請求資源而阻塞時,對已獲得的資源保持不放。
- 不可強行佔有:程序(執行緒)已獲得的資源,在未使用完之前,不能強行剝奪。
- 迴圈等待條件:若干程序(執行緒)之間形成一種頭尾相接的迴圈等待資源關係。
這四個條件是死鎖的必要條件,只要系統發生死鎖,這些條件必然成立,而只要上述條件之一不滿足,就不會發生死鎖。
1:通過jstack定位死鎖資訊
1.1:編寫死鎖程式碼
Locklock1=newReentrantLock(); Locklock2=newReentrantLock(); ExecutorServiceexectuorService=Executors.newFixedThreadPool(2); exectuorService.submit(()->{ lock1.lock(); try{ Thread.sleep(1000); }catch(Exceptione){} try{} finally{ lock1.unlock(); lock2.unlock(); } }); exectuorService.submit(()->{ lock2.lock(); try{ Thread.sleep(1000); }catch(Exceptione){} try{} finally{ lock1.unlock(); lock2.unlock(); } });
1.2:檢視死鎖執行緒的pid
- jps檢視死鎖的執行緒pid
- 使用 jstack -l pid 檢視死鎖資訊
- 通過列印資訊我們可以找到發生死鎖的程式碼是在哪個位置
"DestroyVM"#13prio=5os_prio=31tid=0x00007f9a1d8fe800nid=0xd03waitingoncondition[0x0000000000000000] java.lang.Thread.State:RUNNABLE Lockedownablesynchronizers: -None "pool-1-thread-2"#12prio=5os_prio=31tid=0x00007f9a1d8fe000nid=0xa703waitingoncondition[0x000070000ff8e000] java.lang.Thread.State:WAITING(parking) atsun.misc.Unsafe.park(NativeMethod) -parkingtowaitfor<0x0000000795768cd8>(ajava.util.concurrent.locks.ReentrantLock$NonfairSync) atjava.util.concurrent.locks.LockSupport.park(LockSupport.java:175) atjava.util.concurrent.locks.AbstractQueuedSynchronizer.parkAndCheckInterrupt(AbstractQueuedSynchronizer.java:836) atjava.util.concurrent.locks.AbstractQueuedSynchronizer.acquireQueued(AbstractQueuedSynchronizer.java:870) atjava.util.concurrent.locks.AbstractQueuedSynchronizer.acquire(AbstractQueuedSynchronizer.java:1199) atjava.util.concurrent.locks.ReentrantLock$NonfairSync.lock(ReentrantLock.java:209) atjava.util.concurrent.locks.ReentrantLock.lock(ReentrantLock.java:285) atcom.coco.util.SlideTimeUnit.lambda$main$1(SlideTimeUnit.java:63) atcom.coco.util.SlideTimeUnit$$Lambda$2/565760380.run(UnknownSource) atjava.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511) atjava.util.concurrent.FutureTask.run(FutureTask.java:266) atjava.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) &nbshttp://www.cppcns.comp;atjava.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) atjava.lang.Thread.run(Thread.java:748) Lockedownablesynchronizers: -<0x0000000QfKtgOJ795768d08>(ajava.util.concurrent.locks.ReentrantLock$NonfairSync) -<0x0000000795a9e4e0>(ajava.util.concurrent.ThreadPoolExecutor$Worker) "pool-1-thread-1"#11prio=5os_prio=31tid=0x00007f9a2082c800nid=0xa803waitingoncondition[0x000070000fe8b000] java.lang.Thread.State:WAITING(parking) atsun.misc.Unsafe.park(NativeMethod) -parkingtowaitfor<0x0000000795768d08>(ajava.util.concurrent.locks.ReentrantLock$NonfairSync) atjava.util.concurrent.locks.LockSupport.park(LockSupport.java:175) atjava.util.concurrent.locks.AbstractQueuedSynchronizer.parkAndCheckInterrupt(AbstractQueuedSynchronizer.java:836) atjava.util.concurrent.locks.AbstractQueuedSynchronizer.acquireQueued(AbstractQueuedSynchronizer.java:870) atjava.util.concurrent.locks.AbstractQueuedSynchronizer.acquire(AbstractQueuedSynchronizer.java:1199) atjava.util.concurrent.locks.ReentrantLock$NonfairSync.lock(ReentrantLock.java:209) atjava.util.concurrent.locks.ReentrantLock.lock(ReentrantLock.java:285) atcom.coco.util.SlideTimeUnit.lambda$main$0(SlideTimeUnit.java:49) atcom.coco.util.SlideTimeUnit$$Lambda$1/596512129.run(UnknownSource) atjava.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511) atjava.util.concurrent.FutureTask.run(FutureTask.java:266) atjava.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) atjava.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) atjava.lang.Thread.run(Thread.java:748) Lockedownablesynchronizers: -<0x0000000795768cd8>(ajava.util.concurrent.locks.ReentrantLock$NonfairSync) -<0x0000000795a9ba28>(ajhttp://www.cppcns.comava.util.concurrQfKtgOJent.ThreadPoolExecutor$Worker) "ServiceThread"#10daemonprio=9os_prio=31tid=0x00007f9a2082c000nid=0x4103runnable[0x0000000000000000] java.lang.Thread.State:RUNNABLE Lockedownablesynchronizers: -None "C1CompilerThread3"#9daemonprio=9os_prio=31tid=0x00007f9a1e021800nid=0x3f03waitingoncondition[0x0000000000000000] java.lang.Thread.State:RUNNABLE
2:通過Arthas工具定位死鎖
2.1: 下載好Arthas的jar,然後執行
有一個 thread -b 就可以檢視到死鎖資訊
[arthas@4182]$thread-b "pool-1-thread-2"Id=12WAITINGonjava.util.concurrent.locks.ReentrantLock$NonfairSync@2cb8a9a3ownedby"pool-1-thread-1"Id=11 atsun.misc.Unsafe.park(NativeMethod) -waitingonjava.util.concurrent.locks.ReentrantLock$NonfairSync@2cb8a9a3 atjava.util.concurrent.locks.LockSupport.park(LockSupport.java:175) atjava.util.concurrent.locks.AbstractQueuedSynchronizer.parkAndCheckInterrupt(AbstractQueuedSynchronizer.java:836) atjava.util.concurrent.locks.AbstractQueuedSynchronizer.acquireQueued(AbstractQueuedSynchronizer.java:870) atjava.util.concurrent.locks.AbstractQueuedSynchronizer.acquire(AbstractQueuedSynchronizer.java:1199) atjava.util.concurrent.locks.ReentrantLock$NonfairSync.lock(ReentrantLock.java:209) atjava.util.concurrent.locks.ReentrantLock.lock(ReentrantLock.java:285) atcom.coco.util.SlideTimeUnit.lambda$main$1(SlideTimeUnit.java:63) atcom.coco.util.SlideTimeUnit$$Lambda$2/565760380.run(UnknownSource) atjava.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511) atjava.util.concurrent.FutureTask.run(FutureTask.java:266) atjava.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) atjava.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) atjava.lang.Thread.run(Thread.java:748) Numberoflockedsynchronizers=2 -java.util.concurrent.ThreadPoolExecutor$Worker@6433a2 -java.util.concurrent.locks.ReentrantLock$NonfairSync@3a855d13<----butblocks1otherthreads!
3. 通過 Jvisualvm 定位死鎖
Jvisualvm 是一種自帶的視覺化工具,往往在在本地執行。
通過 Jvisualvm 命令開啟軟體,選中程序,進入執行緒檢視,會給出死鎖提示:
死鎖的預防
- 儘量避免使用多個鎖,並且只有需要時才持有鎖。
- 如果使用多個鎖,一定要設計好鎖的獲取順序。
- 使用帶有超時的方法,為程式帶來更多的可控性,比如指定獲取鎖的時間最多為5秒,超時就放棄。
- 通過一些程式碼靜態檢查工具發現可能存在的死鎖問題,比如FindBugs。
總結
到此這篇關於java定位死鎖的文章就介紹到這了,更多相關java定位死鎖內容請搜尋我們以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援我們!