什么是死锁
死锁就是两个或多个线程并行执行时,彼此等待对方持有的资源,从而形成了一种僵持状态。
死锁形成有4个条件:
- 互斥
- 持有资源并等待
- 不可剥夺
- 环路等待
示例
我们创建两个线程,分别为线程A和线程B。
在线程A中,将获取资源A,睡眠一秒,然后获取资源B。
在线程B中,将获取资源B,睡眠一秒,然后获取资源A。
代码示例如下:
public class DeadLockDemo {
private static Object resourceA = new Object();
private static Object resourceB = new Object();
public static void main(String[] args) {
Thread threadA = createThreadA();
Thread threadB = createThreadB();
threadA.start();
threadB.start();
}
private static Thread createThreadA() {
Thread threadA = new Thread(() -> {
synchronized (resourceA) {
System.out.println(Thread.currentThread() + " got ResourceA");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread() + " waiting get ResourceB");
synchronized (resourceB) {
System.out.println(Thread.currentThread() + " got ResourceB");
}
}
}, "ThreadA");
return threadA;
}
private static Thread createThreadB() {
Thread threadA = new Thread(() -> {
synchronized (resourceB) {
System.out.println(Thread.currentThread() + " got ResourceB");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread() + " waiting get ResourceA");
synchronized (resourceA) {
System.out.println(Thread.currentThread() + " got ResourceA");
}
}
}, "ThreadB");
return threadA;
}
}
- 执行结果
Thread[ThreadA,5,main] got ResourceA
Thread[ThreadB,5,main] got ResourceB
Thread[ThreadB,5,main] waiting get ResourceA
Thread[ThreadA,5,main] waiting get ResourceB
工具检测
jstack命令可以用来检测死锁信息。
执行命令 jstack <PID>
, 得到相关信息如下:
Found one Java-level deadlock:
=============================
"ThreadB":
waiting to lock monitor 0x00007f97a3002cb8 (object 0x000000076ac74998, a java.lang.Object),
which is held by "ThreadA"
"ThreadA":
waiting to lock monitor 0x00007f97a3005548 (object 0x000000076ac749a8, a java.lang.Object),
which is held by "ThreadB"
Java stack information for the threads listed above:
===================================================
"ThreadB":
at com.hornsey.learn.concurrent.DeadLockDemo.lambda$createThreadB$1(DeadLockDemo.java:54)
- waiting to lock <0x000000076ac74998> (a java.lang.Object)
- locked <0x000000076ac749a8> (a java.lang.Object)
at com.hornsey.learn.concurrent.DeadLockDemo$$Lambda$2/1706234378.run(Unknown Source)
at java.lang.Thread.run(Thread.java:748)
"ThreadA":
at com.hornsey.learn.concurrent.DeadLockDemo.lambda$createThreadA$0(DeadLockDemo.java:35)
- waiting to lock <0x000000076ac749a8> (a java.lang.Object)
- locked <0x000000076ac74998> (a java.lang.Object)
at com.hornsey.learn.concurrent.DeadLockDemo$$Lambda$1/1198108795.run(Unknown Source)
at java.lang.Thread.run(Thread.java:748)
Found 1 deadlock.
通过jstack的输出结果可知,线程A在等待线程B持有的锁,线程B在等待线程A持有的锁,结果形成了死锁。
如何解决
资源有序分配法
前面讲到,死锁形成有4个条件。我们只想破坏其中一个条件,就可以避免死锁。场景的解决死锁的办法就是资源有序分配法,来破坏死锁的环路条件。
比如上面的例子中,线程B获取资源的顺序改成同样先获取资源A,再获取资源B,则程序就可以正常执行了。
执行结果如下:
Thread[ThreadA,5,main] got ResourceA
Thread[ThreadA,5,main] waiting get ResourceB
Thread[ThreadA,5,main] got ResourceB
Thread[ThreadB,5,main] got ResourceA
Thread[ThreadB,5,main] waiting get ResourceB
Thread[ThreadB,5,main] got ResourceB