死锁是多线程互相持有对方所需锁而全部阻塞的现象,需同时满足互斥、占有并等待、不可剥夺、循环等待四个条件;典型代码中两线程以不同顺序获取lockA和lockB导致闭环等待。
死锁不是代码写错了,而是多个线程互相持有对方需要的锁,谁也不肯先放——结果全部卡住不动。
死锁发生的四个必要条件
Java 中的死锁必须同时满足以下四点,缺一不可:
- 互斥:锁是独占的,
synchronized或ReentrantLock都默认满足 - 占有并等待:一个线程已持有一个锁,又去申请另一个锁(比如先
synchronized(A.class),再synchronized(B.class)) - 不可剥夺:Java 中锁不能被强制释放,持有者必须主动退出同步块或调用
unlock() - 循环等待:线程 T1 等 T2 的锁,T2 又等 T1 的锁,形成闭环
最典型的死锁代码长什么样
下面这段代码在多线程环境下极大概率触发死锁:
public class DeadlockExample {
private static final Object lockA = new Object();
private static final Object lockB = new Object();
public static void main(String[] args) {
Thread t1 = new Thread(() -> {
synchronized (lockA) {
try { Thread.sleep(100); } catch (InterruptedException e) {}
synchronized (lockB) {
System.out.println("t1 done");
}
}
});
Thread t2 = new Thread(() -> {
synchronized (lockB) {
try { Thread.sleep(100); } catch (InterruptedException e) {}
synchronized (lockA) {
System.out.println("t2 done");
}
}
});
t1.start(); t2.start();
}
}
关键问题在于:两个线程获取 lockA 和 lockB 的顺序不一致。t1 先 A 后 B,t2 先 B 后 A —— 这就是循环等待的温床。