目錄
- 一、產生死鎖的原因
- 二、如何避免死鎖
一、產生死鎖的原因
發生死鎖的情況:
多個線程需要同時占用多個共享資源而發生需要互相死循環等待的情況,就是,兩個線程互相等待著對象釋放鎖,一直這樣僵持下去,所以導致了死鎖的產生。
產生死鎖代碼:
package com.javaagent.thread;public class DieThread implements Runnable {public static Object obj1=new Object();public static Object obj2=new Object();private boolean flag;public DieThread(boolean bl){flag = bl;}@Overridepublic void run() {if(flag) {while(true) {synchronized(obj1) {try {Thread.sleep(1000);}catch (InterruptedException e){e.printStackTrace();}System.out.println("線程" + Thread.currentThread().getName() + "獲取obj1鎖對象,等待獲取obj2鎖對象...");synchronized(obj2) {System.out.println(Thread.currentThread().getName() + " ---- obj2.");}}}}else {while(true){synchronized(obj2) {System.out.println("線程" + Thread.currentThread().getName() + "獲取obj2鎖對象,等待獲取obj1鎖對象...");synchronized(obj1) {System.out.println(Thread.currentThread().getName() + " ---- obj1.");}}}}}
}
public class Main {public static void main(String[] args){DieThread d1=new DieThread(true);DieThread d2=new DieThread(false);final Thread t1 = new Thread(d1);final Thread t2 = new Thread(d2);t1.setName("DieThread_1");t2.setName("DieThread_2");t1.start();t2.start();}
}
輸出結果:
線程DieThread_2獲取obj2鎖對象,等待獲取obj1鎖對象...
線程DieThread_1獲取obj1鎖對象,等待獲取obj2鎖對象...
采用 visualVM工具
來進行診斷,結果如下圖所示:
從該圖中,我們可以看出,Main的 進程ID為84114
,紅框1中提示 檢測到死鎖
,然后我們點擊紅框3來查看 線程Dump
數據,如下圖:
詳細信息:
2020-03-04 15:02:44
Full thread dump Java HotSpot(TM) 64-Bit Server VM (25.121-b13 mixed mode):"RMI TCP Connection(idle)" #19 daemon prio=9 os_prio=31 tid=0x00007ff1db108800 nid=0x5f03 waiting on condition [0x0000700001961000]java.lang.Thread.State: TIMED_WAITING (parking)at sun.misc.Unsafe.park(Native Method)- parking to wait for <0x0000000796082e28> (a java.util.concurrent.SynchronousQueue$TransferStack)at java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:215)at java.util.concurrent.SynchronousQueue$TransferStack.awaitFulfill(SynchronousQueue.java:460)at java.util.concurrent.SynchronousQueue$TransferStack.transfer(SynchronousQueue.java:362)at java.util.concurrent.SynchronousQueue.poll(SynchronousQueue.java:941)at java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1066)at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1127)at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)at java.lang.Thread.run(Thread.java:745)Locked ownable synchronizers:- None"JMX server connection timeout 18" #18 daemon prio=9 os_prio=31 tid=0x00007ff1da87c800 nid=0x5d03 in Object.wait() [0x000070000185e000]java.lang.Thread.State: TIMED_WAITING (on object monitor)at java.lang.Object.wait(Native Method)at com.sun.jmx.remote.internal.ServerCommunicatorAdmin$Timeout.run(ServerCommunicatorAdmin.java:168)- locked <0x00000007964a9dc8> (a [I)at java.lang.Thread.run(Thread.java:745)Locked ownable synchronizers:- None"RMI Scheduler(0)" #17 daemon prio=9 os_prio=31 tid=0x00007ff1da895800 nid=0x5b03 waiting on condition [0x000070000175b000]java.lang.Thread.State: TIMED_WAITING (parking)at sun.misc.Unsafe.park(Native Method)- parking to wait for <0x00000007960ab5d8> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)at java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:215)at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.awaitNanos(AbstractQueuedSynchronizer.java:2078)at java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.take(ScheduledThreadPoolExecutor.java:1093)at java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.take(ScheduledThreadPoolExecutor.java:809)at java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1067)at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1127)at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)at java.lang.Thread.run(Thread.java:745)Locked ownable synchronizers:- None"RMI TCP Connection(1)-10.0.5.39" #16 daemon prio=9 os_prio=31 tid=0x00007ff1d99ed800 nid=0x5903 runnable [0x0000700001657000]java.lang.Thread.State: RUNNABLEat 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 java.io.BufferedInputStream.fill(BufferedInputStream.java:246)at java.io.BufferedInputStream.read(BufferedInputStream.java:265)- locked <0x00000007963eced8> (a java.io.BufferedInputStream)at java.io.FilterInputStream.read(FilterInputStream.java:83)at sun.rmi.transport.tcp.TCPTransport.handleMessages(TCPTransport.java:550)at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run0(TCPTransport.java:826)at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.lambda$run$0(TCPTransport.java:683)at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler$$Lambda$3/657644190.run(Unknown Source)at java.security.AccessController.doPrivileged(Native Method)at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run(TCPTransport.java:682)at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)at java.lang.Thread.run(Thread.java:745)Locked ownable synchronizers:- <0x0000000796299c10> (a java.util.concurrent.ThreadPoolExecutor$Worker)"RMI TCP Accept-0" #15 daemon prio=9 os_prio=31 tid=0x00007ff1d9941800 nid=0x570f runnable [0x0000700001555000]java.lang.Thread.State: RUNNABLEat java.net.PlainSocketImpl.socketAccept(Native Method)at java.net.AbstractPlainSocketImpl.accept(AbstractPlainSocketImpl.java:409)at java.net.ServerSocket.implAccept(ServerSocket.java:545)at java.net.ServerSocket.accept(ServerSocket.java:513)at sun.management.jmxremote.LocalRMIServerSocketFactory$1.accept(LocalRMIServerSocketFactory.java:52)at sun.rmi.transport.tcp.TCPTransport$AcceptLoop.executeAcceptLoop(TCPTransport.java:400)at sun.rmi.transport.tcp.TCPTransport$AcceptLoop.run(TCPTransport.java:372)at java.lang.Thread.run(Thread.java:745)Locked ownable synchronizers:- None"Attach Listener" #13 daemon prio=9 os_prio=31 tid=0x00007ff1db028800 nid=0x5303 waiting on condition [0x0000000000000000]java.lang.Thread.State: RUNNABLELocked ownable synchronizers:- None"DestroyJavaVM" #12 prio=5 os_prio=31 tid=0x00007ff1da1ac000 nid=0x1403 waiting on condition [0x0000000000000000]java.lang.Thread.State: RUNNABLELocked ownable synchronizers:- None"DieThread_2" #11 prio=5 os_prio=31 tid=0x00007ff1d9839800 nid=0x5103 waiting for monitor entry [0x000070000134f000]java.lang.Thread.State: BLOCKED (on object monitor)at com.javaagent.thread.DieThread.run(DieThread.java:37)- waiting to lock <0x000000079585bee0> (a java.lang.Object)- locked <0x000000079585bef0> (a java.lang.Object)at java.lang.Thread.run(Thread.java:745)Locked ownable synchronizers:- None"DieThread_1" #10 prio=5 os_prio=31 tid=0x00007ff1d9835800 nid=0x4f03 waiting for monitor entry [0x000070000124c000]java.lang.Thread.State: BLOCKED (on object monitor)at com.javaagent.thread.DieThread.run(DieThread.java:26)- waiting to lock <0x000000079585bef0> (a java.lang.Object)- locked <0x000000079585bee0> (a java.lang.Object)at java.lang.Thread.run(Thread.java:745)Locked ownable synchronizers:- None"Service Thread" #9 daemon prio=9 os_prio=31 tid=0x00007ff1d9835000 nid=0x4b03 runnable [0x0000000000000000]java.lang.Thread.State: RUNNABLELocked ownable synchronizers:- None"C1 CompilerThread2" #8 daemon prio=9 os_prio=31 tid=0x00007ff1da87f800 nid=0x4903 waiting on condition [0x0000000000000000]java.lang.Thread.State: RUNNABLELocked ownable synchronizers:- None"C2 CompilerThread1" #7 daemon prio=9 os_prio=31 tid=0x00007ff1d98c5800 nid=0x4703 waiting on condition [0x0000000000000000]java.lang.Thread.State: RUNNABLELocked ownable synchronizers:- None"C2 CompilerThread0" #6 daemon prio=9 os_prio=31 tid=0x00007ff1da886000 nid=0x4503 waiting on condition [0x0000000000000000]java.lang.Thread.State: RUNNABLELocked ownable synchronizers:- None"Monitor Ctrl-Break" #5 daemon prio=5 os_prio=31 tid=0x00007ff1da884800 nid=0x4303 runnable [0x0000700000c3a000]java.lang.Thread.State: RUNNABLEat 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 <0x00000007958fc840> (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 <0x00000007958fc840> (a java.io.InputStreamReader)at java.io.BufferedReader.readLine(BufferedReader.java:389)at com.intellij.rt.execution.application.AppMainV2$1.run(AppMainV2.java:64)Locked ownable synchronizers:- None"Signal Dispatcher" #4 daemon prio=9 os_prio=31 tid=0x00007ff1db029800 nid=0x360f runnable [0x0000000000000000]java.lang.Thread.State: RUNNABLELocked ownable synchronizers:- None"Finalizer" #3 daemon prio=8 os_prio=31 tid=0x00007ff1d9820800 nid=0x3003 in Object.wait() [0x000070000092e000]java.lang.Thread.State: WAITING (on object monitor)at java.lang.Object.wait(Native Method)- waiting on <0x0000000795588ec8> (a java.lang.ref.ReferenceQueue$Lock)at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:143)- locked <0x0000000795588ec8> (a java.lang.ref.ReferenceQueue$Lock)at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:164)at java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:209)Locked ownable synchronizers:- None"Reference Handler" #2 daemon prio=10 os_prio=31 tid=0x00007ff1d9810800 nid=0x2e03 in Object.wait() [0x000070000082b000]java.lang.Thread.State: WAITING (on object monitor)at java.lang.Object.wait(Native Method)- waiting on <0x0000000795586b68> (a java.lang.ref.Reference$Lock)at java.lang.Object.wait(Object.java:502)at java.lang.ref.Reference.tryHandlePending(Reference.java:191)- locked <0x0000000795586b68> (a java.lang.ref.Reference$Lock)at java.lang.ref.Reference$ReferenceHandler.run(Reference.java:153)Locked ownable synchronizers:- None"VM Thread" os_prio=31 tid=0x00007ff1da026800 nid=0x2c03 runnable "GC task thread#0 (ParallelGC)" os_prio=31 tid=0x00007ff1d9810000 nid=0x2403 runnable "GC task thread#1 (ParallelGC)" os_prio=31 tid=0x00007ff1da00d000 nid=0x2603 runnable "GC task thread#2 (ParallelGC)" os_prio=31 tid=0x00007ff1da00d800 nid=0x2803 runnable "GC task thread#3 (ParallelGC)" os_prio=31 tid=0x00007ff1da00e000 nid=0x2a03 runnable "VM Periodic Task Thread" os_prio=31 tid=0x00007ff1da80d000 nid=0x4d03 waiting on condition JNI global references: 354Found one Java-level deadlock:
=============================
"DieThread_2":waiting to lock monitor 0x00007ff1da80ad58 (object 0x000000079585bee0, a java.lang.Object),which is held by "DieThread_1"
"DieThread_1":waiting to lock monitor 0x00007ff1da808418 (object 0x000000079585bef0, a java.lang.Object),which is held by "DieThread_2"Java stack information for the threads listed above:
===================================================
"DieThread_2":at com.javaagent.thread.DieThread.run(DieThread.java:37)- waiting to lock <0x000000079585bee0> (a java.lang.Object)- locked <0x000000079585bef0> (a java.lang.Object)at java.lang.Thread.run(Thread.java:745)
"DieThread_1":at com.javaagent.thread.DieThread.run(DieThread.java:26)- waiting to lock <0x000000079585bef0> (a java.lang.Object)- locked <0x000000079585bee0> (a java.lang.Object)at java.lang.Thread.run(Thread.java:745)Found 1 deadlock.
通過上面的線程Dump文件信息,我們可以看出來發生了死鎖,下面也列出了java級別的鎖問題和棧信息:
Found one Java-level deadlock:
=============================
"DieThread_2":waiting to lock monitor 0x00007ff1da80ad58 (object 0x000000079585bee0, a java.lang.Object),which is held by "DieThread_1"
"DieThread_1":waiting to lock monitor 0x00007ff1da808418 (object 0x000000079585bef0, a java.lang.Object),which is held by "DieThread_2"Java stack information for the threads listed above:
===================================================
"DieThread_2":at com.javaagent.thread.DieThread.run(DieThread.java:37)- waiting to lock <0x000000079585bee0> (a java.lang.Object)- locked <0x000000079585bef0> (a java.lang.Object)at java.lang.Thread.run(Thread.java:745)
"DieThread_1":at com.javaagent.thread.DieThread.run(DieThread.java:26)- waiting to lock <0x000000079585bef0> (a java.lang.Object)- locked <0x000000079585bee0> (a java.lang.Object)at java.lang.Thread.run(Thread.java:745)Found 1 deadlock.
并且棧信息中,可以定位到死鎖發生的地方。
如果解決死鎖,那是因為代碼的邏輯存在問題,可以調整代碼的邏輯或者業務,來避免死鎖。
二、如何避免死鎖
-
避免一個線程同時獲取多個鎖
-
避免一個線程在鎖內同時占用多個資源,盡量保證每個鎖只占用一個資源
-
嘗試使用定時鎖,使用
lock.tryLock(timeout)
來替代使用內部鎖機制。 -
對于數據庫鎖,加鎖和解鎖必須在一個數據庫連接里,否則會出現解鎖失敗的情況。