> 文章列表 > Java 死锁的原理、检测和解决死锁

Java 死锁的原理、检测和解决死锁

Java 死锁的原理、检测和解决死锁

什么是死锁

两个或者多个线程互相持有对方所需要的资源(锁),都在等待对方执行完毕才能继续往下执行的时候,就称为发生了死锁,结果就是两个进程都陷入了无限的等待中。

一般是有多个锁对象的情况下并且获得锁顺序不一致造成的。

死锁产生的条件

死锁产生有四个必要条件,只要系统发生死锁则以上四个条件都必须成立。

  1. 互斥条件: 资源是独占的且排他使用,线程互斥使用资源,即任意时刻一个资源只能给一个线程使用,其他线程若申请一个资源,而该资源被另一线程占有时,则申请者等待直到资源被占有者释放。
  2. 不可剥夺条件: 线程所获得的资源在未使用完毕之前,不被其他线程强行剥夺,而只能由获得该资源的线程资源释放。
  3. 请求和保持条件: 线程每次申请它所需要的资源,在申请新的资源的同时,继续占用已分配到的资源。
  4. 循环等待条件: 在发生死锁时必然存在一个线程等待队列{P1,P2,…,Pn},其中P1等待P2占有的资源,P2等待P3占有的资源,…,Pn等待P1占有的资源,形成一个线程等待环路,环路中每一个线程所占有的资源同时被另一个申请,也就是前一个线程占有后一个线程所申请的资源。

检测死锁

构造死锁

public class DeadLockTest {public static Object lockObj1 = new Object();public static Object lockObj2 = new Object();public static void main(String[] args) {Runnable r1 = DeadLockTest::lock1;Thread t1 = new Thread(r1);t1.setName("deadLockThread1");Runnable r2 = DeadLockTest::lock2;Thread t2 = new Thread(r2);t2.setName("deadLockThread2");t1.start();t2.start();}public static void lock1() {try {synchronized (lockObj1) {System.out.println(Thread.currentThread().getName() + "进入lock1");TimeUnit.SECONDS.sleep(5);lock2();}} catch (InterruptedException e) {e.printStackTrace();}}public static void lock2() {try {synchronized (lockObj2) {System.out.println(Thread.currentThread().getName() + "进入lock2");TimeUnit.SECONDS.sleep(5);lock1();}} catch (InterruptedException e) {e.printStackTrace();}}
}

检测死锁

JConsole

JConsole是Java 5 开始提供。

启动JConsole: JAVA_HOME\\bin\\jconsole.exe
Java 死锁的原理、检测和解决死锁
Java 死锁的原理、检测和解决死锁
Java 死锁的原理、检测和解决死锁

JStack

JStack位于:JAVA_HOME\\bin\\jstack.exe

Found one Java-level deadlock: 描述了死锁线程名称和等待的锁持有者;
Java stack information for the threads listed above:描述了死锁线程的堆栈信息。

Administrator@DESKTOP-CKPKB5K MINGW64 ~
$ jps
4512 GradleDaemon
12136 Launcher
13592
16760 Jps
15916 DeadLockTestAdministrator@DESKTOP-CKPKB5K MINGW64 ~
$ jstack -l 15916|grep 'deadlock' -A 50
Found one Java-level deadlock:
=============================
"deadLockThread2":waiting to lock monitor 0x0000021b98303fb8 (object 0x00000000f9c9ae20, a java.lang.Object),which is held by "deadLockThread1"
"deadLockThread1":waiting to lock monitor 0x0000021b98301938 (object 0x00000000f9c9ae30, a java.lang.Object),which is held by "deadLockThread2"Java stack information for the threads listed above:
===================================================
"deadLockThread2":at tech.pplus.cases.lock.DeadLockTest.lock1(DeadLockTest.java:33)- waiting to lock <0x00000000f9c9ae20> (a java.lang.Object)at tech.pplus.cases.lock.DeadLockTest.lock2(DeadLockTest.java:49)- locked <0x00000000f9c9ae30> (a java.lang.Object)at tech.pplus.cases.lock.DeadLockTest$$Lambda$2/1104106489.run(Unknown Source)at java.lang.Thread.run(Thread.java:750)
"deadLockThread1":at tech.pplus.cases.lock.DeadLockTest.lock2(DeadLockTest.java:47)- waiting to lock <0x00000000f9c9ae30> (a java.lang.Object)at tech.pplus.cases.lock.DeadLockTest.lock1(DeadLockTest.java:35)- locked <0x00000000f9c9ae20> (a java.lang.Object)at tech.pplus.cases.lock.DeadLockTest$$Lambda$1/2047329716.run(Unknown Source)at java.lang.Thread.run(Thread.java:750)Found 1 deadlock.

解决死锁

使用Lock#tryLock(long, java.util.concurrent.TimeUnit)

破坏死锁产生的“循环等待条件”条件,设置最大等待锁时间以及处理获取锁失败。

改造代码锁的实现:

public class SolveDeadLockTest {public static ReentrantLock lockObj1 = new ReentrantLock();public static ReentrantLock lockObj2 = new ReentrantLock();;public static void main(String[] args) {Runnable r1 = SolveDeadLockTest::lock1;Thread t1 = new Thread(r1);t1.setName("deadLockThread1");Runnable r2 = SolveDeadLockTest::lock2;Thread t2 = new Thread(r2);t2.setName("deadLockThread2");t1.start();t2.start();}public static void lock1() {try {if (lockObj1.tryLock(3, TimeUnit.SECONDS)) {System.out.println(Thread.currentThread().getName() + "进入lock1");TimeUnit.SECONDS.sleep(5);lock2();}else {System.out.println(Thread.currentThread().getName() + "进入lock1失败,原因:获取锁失败!");}} catch (InterruptedException e) {e.printStackTrace();}}public static void lock2() {try {if (lockObj2.tryLock(3, TimeUnit.SECONDS)) {System.out.println(Thread.currentThread().getName() + "进入lock2");TimeUnit.SECONDS.sleep(5);lock1();}else {System.out.println(Thread.currentThread().getName() + "进入lock2失败,原因:获取锁失败!");}} catch (InterruptedException e) {e.printStackTrace();}}
}

运行结果:

deadLockThread1进入lock1
deadLockThread2进入lock2
deadLockThread2进入lock1失败,原因:获取锁失败!
deadLockThread1进入lock2失败,原因:获取锁失败!Process finished with exit code 0

线程都正常结束,并没有产生死锁。