产生死锁的条件:
- 互斥:资源必须处于非共享模式,即一次只有一个进程可以使用。如果另一进程申请该资源,那么必须等待直到该资源被释放为止。
- 占有并等待:一个进程至少应该占有一个资源,并等待另一资源,而该资源被其他进程所占有。
- 非抢占:资源不能被抢占。只能在持有资源的进程完成任务后,该资源才会被释放。
- 循环等待:有一组等待进程
{P0, P1,..., Pn}
,P0
等待的资源被P1
占有,P1
等待的资源被P2
占有,……,Pn-1
等待的资源被Pn
占有,Pn
等待的资源被P0
占有。
这四个条件是产生死锁的 必要条件 ,也就是说只要系统发生死锁,这些条件必然成立,而只要上述条件之一不满足,就不会发生死锁。
避免或预防死锁的方法:
- 破坏互斥条件:
- 尽量减少资源的独占性,使资源可以共享。例如,通过增加资源的可并发性来减少互斥资源的数量。
- 破坏持有并等待条件:
- 在进程开始时一次性请求所有需要的资源,或者在持有资源时不再请求新的资源。这样可以避免持有资源并等待其他资源的情况。
- 破坏不可剥夺条件:
- 允许资源的剥夺。当一个进程持有资源但无法获得其他所需资源时,可以暂时释放已经持有的资源,使其他进程可以利用这些资源。
- 破坏循环等待条件:
- 通过对资源进行编号,并要求进程按序号递增的顺序请求资源。这样可以破坏循环等待的形成。例如,规定所有进程在请求资源时必须先请求编号较小的资源,再请求编号较大的资源。
死锁示例:
public class DeadLockDemo {
private static final Object lock1 = new Object();
private static final Object lock2 = new Object();
public static void main(String[] args) {
new Thread(() -> {
// 线程1先获取锁1,再获取锁2
synchronized (lock1) {
System.out.println(Thread.currentThread().getName() + "获取到锁1");
try {
// 为了让线程2有机会获取锁2, 线程1休眠1秒
Thread.sleep(1000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
// 线程1获取锁1后,尝试获取锁2
synchronized (lock2) {
System.out.println(Thread.currentThread().getName() + "获取到锁2");
}
}
}, "线程1").start();
new Thread(() -> {
// 线程2先获取锁2,再获取锁1
synchronized (lock2) {
System.out.println(Thread.currentThread().getName() + "获取到锁2");
try {
// 为了让线程1有机会获取锁1, 线程2休眠1秒
Thread.sleep(1000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
// 线程2获取锁2后,尝试获取锁1
synchronized (lock1) {
System.out.println(Thread.currentThread().getName() + "获取到锁1");
}
}
}, "线程2").start();
// 检测死锁
while (true) {
// 通过jps命令查看进程号
// 通过jstack命令查看进程号对应的线程堆栈信息
}
}
}
通过 jps
和 jstack
检查死锁情况,控制台输出以下结果,可以看到发生了一处死锁:
Found one Java-level deadlock:
=============================
"线程1":
waiting to lock monitor 0x000002644a60de00 (object 0x00000006214d59b0, a java.lang.Object),
which is held by "线程2"
"线程2":
waiting to lock monitor 0x000002644a60c400 (object 0x00000006214d59a0, a java.lang.Object),
which is held by "线程1"
Java stack information for the threads listed above:
===================================================
"线程1":
at com.example.dev.DeadLockDemo.lambda$main$0(DeadLockDemo.java:21)
- waiting to lock <0x00000006214d59b0> (a java.lang.Object)
- locked <0x00000006214d59a0> (a java.lang.Object)
at com.example.dev.DeadLockDemo$$Lambda$14/0x0000000800066840.run(Unknown Source)
at java.lang.Thread.run(java.base@11.0.2/Thread.java:834)
"线程2":
at com.example.dev.DeadLockDemo.lambda$main$1(DeadLockDemo.java:39)
- waiting to lock <0x00000006214d59a0> (a java.lang.Object)
- locked <0x00000006214d59b0> (a java.lang.Object)
at com.example.dev.DeadLockDemo$$Lambda$15/0x0000000800066c40.run(Unknown Source)
at java.lang.Thread.run(java.base@11.0.2/Thread.java:834)
Found 1 deadlock.