JVM?jstack實戰(zhàn)之死鎖問題詳解
如果在生產(chǎn)環(huán)境發(fā)生了死鎖,我們將看到的是部署的程序沒有任何反應了,這個時候我們可以借助 jstack進行分析,下面我們實戰(zhàn)操作查找死鎖的原因。所謂死鎖指的是是一組互相競爭資源的線程因互相等待導致“永久”阻塞的現(xiàn)象。
構造死鎖
編寫代碼,啟動2個線程,Thread1拿到了obj1鎖,準備去拿obj2鎖時,obj2已經(jīng)被Thread2鎖定,所以發(fā)送了死鎖。
public class TestDeadLock {
private static Object obj1 = new Object();
private static Object obj2 = new Object();
public static void main(String[] args) {
new Thread(new Thread1()).start();
new Thread(new Thread2()).start();
}
private static class Thread1 implements Runnable{
@Override
public void run() {
synchronized (obj1){
System.out.println("Thread1 拿到了 obj1 的鎖!");
try {
// 停頓2秒的意義在于,讓Thread2線程拿到obj2的鎖
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (obj2){
System.out.println("Thread1 拿到了 obj2 的鎖!");
}
}
}
}
private static class Thread2 implements Runnable{
@Override
public void run() {
synchronized (obj2){
System.out.println("Thread2 拿到了 obj2 的鎖!");
try {
// 停頓2秒的意義在于,讓Thread1線程拿到obj1的鎖
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (obj1){
System.out.println("Thread2 拿到了 obj1 的鎖!");
}
}
}
}
}
在idea運行
#運行結(jié)果
E:\Java\jdk8u171\bin\java.exe "-javaagent:C:\idea\IntelliJ IDEA 2019.3.2\lib\idea_rt.jar=50268:C:\idea\IntelliJ IDEA 2019.3.2\bin" -Dfile.encoding=UTF-8 -classpath E:\Java\jdk8u171\jre\lib\charsets.jar;E:\Java\jdk8u171\jre\lib\deploy.jar;E:\Java\jdk8u171\jre\lib\ext\access-bridge-64.jar;E:\Java\jdk8u171\jre\lib\ext\cldrdata.jar;E:\Java\jdk8u171\jre\lib\ext\dnsns.jar;E:\Java\jdk8u171\jre\lib\ext\jaccess.jar;E:\Java\jdk8u171\jre\lib\ext\jfxrt.jar;E:\Java\jdk8u171\jre\lib\ext\localedata.jar;E:\Java\jdk8u171\jre\lib\ext\nashorn.jar;E:\Java\jdk8u171\jre\lib\ext\sunec.jar;E:\Java\jdk8u171\jre\lib\ext\sunjce_provider.jar;E:\Java\jdk8u171\jre\lib\ext\sunmscapi.jar;E:\Java\jdk8u171\jre\lib\ext\sunpkcs11.jar;E:\Java\jdk8u171\jre\lib\ext\zipfs.jar;E:\Java\jdk8u171\jre\lib\javaws.jar;E:\Java\jdk8u171\jre\lib\jce.jar;E:\Java\jdk8u171\jre\lib\jfr.jar;E:\Java\jdk8u171\jre\lib\jfxswt.jar;E:\Java\jdk8u171\jre\lib\jsse.jar;E:\Java\jdk8u171\jre\lib\management-agent.jar;E:\Java\jdk8u171\jre\lib\plugin.jar;E:\Java\jdk8u171\jre\lib\resources.jar;E:\Java\jdk8u171\jre\lib\rt.jar;E:\jvm-test\target\classes cn.zjq.jvm.TestDeadLock
Thread1 拿到了 obj1 的鎖!
Thread2 拿到了 obj2 的鎖!
#這里發(fā)生了死鎖,程序一直將等待下去
使用jstack進行分析
C:\Users\dell>jps
12624 Launcher
5140 org.eclipse.equinox.launcher_1.5.0.v20180512-1130.jar
7204
17000 Jps
18536 KotlinCompileDaemon
17196 TestDeadLock
29916 RemoteMavenServer
C:\Users\dell> jstack 17196
2021-07-25 21:52:25
Full thread dump Java HotSpot(TM) 64-Bit Server VM (25.171-b11 mixed mode):
"DestroyJavaVM" #14 prio=5 os_prio=0 tid=0x00000000029f2800 nid=0x1868 waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"Thread-1" #13 prio=5 os_prio=0 tid=0x000000001e5d3000 nid=0x6994 waiting for monitor entry [0x000000001f49f000]
java.lang.Thread.State: BLOCKED (on object monitor)
at cn.zjq.jvm.TestDeadLock$Thread2.run(TestDeadLock.java:49)
- waiting to lock <0x000000076b688680> (a java.lang.Object)
- locked <0x000000076b688690> (a java.lang.Object)
at java.lang.Thread.run(Thread.java:748)
"Thread-0" #12 prio=5 os_prio=0 tid=0x000000001e5d2000 nid=0x21d8 waiting for monitor entry [0x000000001f39f000]
java.lang.Thread.State: BLOCKED (on object monitor)
at cn.zjq.jvm.TestDeadLock$Thread1.run(TestDeadLock.java:29)
- waiting to lock <0x000000076b688690> (a java.lang.Object)
- locked <0x000000076b688680> (a java.lang.Object)
at java.lang.Thread.run(Thread.java:748)
"Service Thread" #11 daemon prio=9 os_prio=0 tid=0x000000001e5aa800 nid=0x6e40 runnable [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"C1 CompilerThread3" #10 daemon prio=9 os_prio=2 tid=0x000000001e516000 nid=0x1828 waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"C2 CompilerThread2" #9 daemon prio=9 os_prio=2 tid=0x000000001e505000 nid=0x712c waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"C2 CompilerThread1" #8 daemon prio=9 os_prio=2 tid=0x000000001e501000 nid=0x7f5c waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"C2 CompilerThread0" #7 daemon prio=9 os_prio=2 tid=0x000000001e4ed000 nid=0x2dc0 waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"Monitor Ctrl-Break" #6 daemon prio=5 os_prio=0 tid=0x000000001e4ff800 nid=0x5040 runnable [0x000000001ec9e000]
java.lang.Thread.State: RUNNABLE
at java.net.SocketInputStream.socketRead0(Native Method)
at java.net.SocketInputStream.socketRead(SocketInputStream.java:116)
at java.net.SocketInputStream.read(SocketInputStream.java:171)
at java.net.SocketInputStream.read(SocketInputStream.java:141)
at sun.nio.cs.StreamDecoder.readBytes(StreamDecoder.java:284)
at sun.nio.cs.StreamDecoder.implRead(StreamDecoder.java:326)
at sun.nio.cs.StreamDecoder.read(StreamDecoder.java:178)
- locked <0x000000076b7d0200> (a java.io.InputStreamReader)
at java.io.InputStreamReader.read(InputStreamReader.java:184)
at java.io.BufferedReader.fill(BufferedReader.java:161)
at java.io.BufferedReader.readLine(BufferedReader.java:324)
- locked <0x000000076b7d0200> (a java.io.InputStreamReader)
at java.io.BufferedReader.readLine(BufferedReader.java:389)
at com.intellij.rt.execution.application.AppMainV2$1.run(AppMainV2.java:64)
"Attach Listener" #5 daemon prio=5 os_prio=2 tid=0x000000001e45b800 nid=0x49d8 waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"Signal Dispatcher" #4 daemon prio=9 os_prio=2 tid=0x000000001e445800 nid=0x6b90 runnable [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"Finalizer" #3 daemon prio=8 os_prio=1 tid=0x000000001e441800 nid=0x2c0c in Object.wait() [0x000000001e91e000]
java.lang.Thread.State: WAITING (on object monitor)
at java.lang.Object.wait(Native Method)
- waiting on <0x000000076b508ed0> (a java.lang.ref.ReferenceQueue$Lock)
at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:143)
- locked <0x000000076b508ed0> (a java.lang.ref.ReferenceQueue$Lock)
at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:164)
at java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:212)
"Reference Handler" #2 daemon prio=10 os_prio=2 tid=0x000000001c54d000 nid=0x6508 in Object.wait() [0x000000001e41f000]
java.lang.Thread.State: WAITING (on object monitor)
at java.lang.Object.wait(Native Method)
- waiting on <0x000000076b506bf8> (a java.lang.ref.Reference$Lock)
at java.lang.Object.wait(Object.java:502)
at java.lang.ref.Reference.tryHandlePending(Reference.java:191)
- locked <0x000000076b506bf8> (a java.lang.ref.Reference$Lock)
at java.lang.ref.Reference$ReferenceHandler.run(Reference.java:153)
"VM Thread" os_prio=2 tid=0x000000001c549000 nid=0x5af4 runnable
"GC task thread#0 (ParallelGC)" os_prio=0 tid=0x0000000002a08800 nid=0x347c runnable
"GC task thread#1 (ParallelGC)" os_prio=0 tid=0x0000000002a0a000 nid=0x6bc8 runnable
"GC task thread#2 (ParallelGC)" os_prio=0 tid=0x0000000002a0b800 nid=0x7d20 runnable
"GC task thread#3 (ParallelGC)" os_prio=0 tid=0x0000000002a0d000 nid=0x728c runnable
"GC task thread#4 (ParallelGC)" os_prio=0 tid=0x0000000002a0f800 nid=0x595c runnable
"GC task thread#5 (ParallelGC)" os_prio=0 tid=0x0000000002a10800 nid=0x43fc runnable
"GC task thread#6 (ParallelGC)" os_prio=0 tid=0x0000000002a14800 nid=0x3c50 runnable
"GC task thread#7 (ParallelGC)" os_prio=0 tid=0x0000000002a16000 nid=0x364c runnable
"VM Periodic Task Thread" os_prio=2 tid=0x000000001e5ca000 nid=0x1da4 waiting on condition
JNI global references: 12
Found one Java-level deadlock:
=============================
"Thread-1":
waiting to lock monitor 0x000000001c5535f8 (object 0x000000076b688680, a java.lang.Object),
which is held by "Thread-0"
"Thread-0":
waiting to lock monitor 0x000000001c550d68 (object 0x000000076b688690, a java.lang.Object),
which is held by "Thread-1"
Java stack information for the threads listed above:
===================================================
"Thread-1":
at cn.zjq.jvm.TestDeadLock$Thread2.run(TestDeadLock.java:49)
- waiting to lock <0x000000076b688680> (a java.lang.Object)
- locked <0x000000076b688690> (a java.lang.Object)
at java.lang.Thread.run(Thread.java:748)
"Thread-0":
at cn.zjq.jvm.TestDeadLock$Thread1.run(TestDeadLock.java:29)
- waiting to lock <0x000000076b688690> (a java.lang.Object)
- locked <0x000000076b688680> (a java.lang.Object)
at java.lang.Thread.run(Thread.java:748)
Found 1 deadlock.
在輸出的信息中,已經(jīng)看到,發(fā)現(xiàn)了1個死鎖,關鍵看這個:

可以清晰的看到:
Thread2獲取了<0x000000076b688690> 的鎖,等待獲取<0x000000076b688680>這個鎖
Thread1獲取了<0x000000076b688680> 的鎖,等待獲取<0x000000076b688690>這個鎖
由此可見,發(fā)生了死鎖。
怎么避免死鎖
死鎖產(chǎn)生的四個必要條件
互斥使用,即當資源被一個線程使用(占有)時,別的線程不能使用
不可搶占,資源請求者不能強制從資源占有者手中奪取資源,資源只能由資源占有者主動釋放。
請求和保持,即當資源請求者在請求其他的資源的同時保持對原有資源的占有。
循環(huán)等待,即存在一個等待隊列:T1等待占有T2的資源,T2等待占有T3的資源,T3等待占有T1的資源。這樣就形成了一個等待環(huán)路。
死鎖產(chǎn)生的原因
系統(tǒng)資源的競爭:通常系統(tǒng)中擁有的不可剝奪資源,其數(shù)量不足以滿足多個進程運行的需要,使得進程在運行過程中,會因爭奪資源而陷入僵局。只有對不可剝奪資源的競爭 才可能產(chǎn)生死鎖,對可剝奪資源的競爭是不會引起死鎖的。
進程推進順序非法:進程在運行過程中,請求和釋放資源的順序不當,也同樣會導致死鎖。
信號量使用不當也會造成死鎖:進程間彼此相互等待對方發(fā)來的消息,結(jié)果也會使得這些進程間無法繼續(xù)向前推進。
死鎖產(chǎn)生的四個必要條件
如何避免死鎖呢
既然發(fā)生死鎖的原因是需要同時滿足四個必要條件,我們只需要打破其中任意一個條件即可避免死鎖問題。
而在這四個條件中第一個互斥條件是無法被破壞的,因為鎖本身就是通過互斥來解決線程安全問題的。
所以對于剩下三個我們可以逐一進行分析
1.加鎖時限
對于不可搶占這個條件占用部分資源的線程進一步申請其他資源時,如果申請不到可以主動釋放它占有的資源,這樣不可搶占這個條件就破壞掉了。線程嘗試獲取鎖的時候加上一定的時限,超過時限則放棄對該鎖的請求,并釋放自己占有的鎖。
2.一次性獲取所有鎖資源
我們可以一次性申請所有的鎖資源,這樣就不存在請求和保持條件了
3.加鎖順序
當多個線程需要相同的一些鎖,但是按照不同的順序加鎖,死鎖就很容易發(fā)生。對于循環(huán)等待這個條件
可以靠按序申請資源來進行預防。
如果能確保所有的線程都是按照相同的順序獲得鎖,那么死鎖就不會發(fā)生。
如果一個線程(比如線程3)需要一些鎖,那么它必須按照確定的順序獲取鎖。它只有獲得了從順序上排在前面的鎖之后,才能獲取后面的鎖。
例如,線程2和線程3只有在獲取了鎖A之后才能嘗試獲取鎖C(譯者注:獲取鎖A是獲取鎖C的必要條件)。因為線程1已經(jīng)擁有了鎖A,所以線程2和3需要一直等到鎖A被釋放。然后在它們嘗試對B或C加鎖之前,必須成功地對A加了鎖。
按照順序加鎖是一種有效的死鎖預防機制。但是,這種方式需要你事先知道所有可能會用到的鎖(譯者注:并對這些鎖做適當?shù)呐判?,但總有些時候是無法預知的。
到此這篇關于JVM jstack實戰(zhàn)之死鎖問題詳解的文章就介紹到這了,更多相關JVM jstack死鎖內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
Java實現(xiàn)的生成二維碼統(tǒng)計掃描次數(shù)并轉(zhuǎn)發(fā)到某個地址功能詳解
這篇文章主要介紹了Java實現(xiàn)的生成二維碼統(tǒng)計掃描次數(shù)并轉(zhuǎn)發(fā)到某個地址功能,可實現(xiàn)生成帶統(tǒng)計功能的二維碼,涉及java二維碼的生成、參數(shù)傳遞、解析等相關操作技巧,需要的朋友可以參考下2018-07-07
jmeter添加自定函數(shù)的實例(jmeter5.3+IntelliJ IDEA)
這篇文章主要介紹了jmeter添加自定函數(shù)的實例(jmeter5.3+IntelliJ IDEA),本文通過圖文并茂的形式給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-11-11

