死锁是多线程互相持有对方所需锁而全部阻塞的现象,需同时满足互斥、占有并等待、不可剥夺、循环等待四个条件;典型代码中两线程以不同顺序获取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 —— 这就是循环等待的温床。
立即学习“Java免费学习笔记(深入)”;
如何检测和避免死锁
JDK 自带工具能帮你发现运行中的死锁:
- 用
jstack查看线程栈,输出里如果出现Found 1 deadlock.就确认了 - 在 JConsole 或 VisualVM 的「线程」页签中点「Detect Deadlock」按钮
- 避免嵌套锁:尽量只在一个同步块内操作,不跨块持锁再申请
- 统一加锁顺序:所有线程按相同顺序获取锁(比如按对象哈希值排序:
if (a.hashCode() ) - 使用带超时的锁:
lock.tryLock(1, TimeUnit.SECONDS)失败就释放已有锁,重试或放弃
真正难处理的不是写出来就死锁的代码,而是那些在高并发、特定时序下才暴露的锁竞争路径——它们往往藏在日志里不报错,只让系统慢慢变慢、响应延迟飙升。










