什么是死锁
两个或者多个线程互相持有对方所需要的资源(锁),都在等待对方执行完毕才能继续往下执行的时候,就称为发生了死锁,结果就是两个进程都陷入了无限的等待中。
一般是有多个锁对象的情况下并且获得锁顺序不一致造成的。
死锁产生的条件
死锁产生有四个必要条件,只要系统发生死锁则以上四个条件都必须成立。
- 互斥条件: 资源是独占的且排他使用,线程互斥使用资源,即任意时刻一个资源只能给一个线程使用,其他线程若申请一个资源,而该资源被另一线程占有时,则申请者等待直到资源被占有者释放。
- 不可剥夺条件: 线程所获得的资源在未使用完毕之前,不被其他线程强行剥夺,而只能由获得该资源的线程资源释放。
- 请求和保持条件: 线程每次申请它所需要的资源,在申请新的资源的同时,继续占用已分配到的资源。
- 循环等待条件: 在发生死锁时必然存在一个线程等待队列{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
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 DeadLockTest
Administrator@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
线程都正常结束,并没有产生死锁。