🧨1、简述
死锁,是多线程并发编程中的常见陷阱。一旦发生,程序将无限等待,严重时可能导致系统冻结或资源耗尽。本文将带你从原理出发,深入理解死锁形成的根源,并给出常见的 解决策略 与 实战样例。
🔍 2、什么是死锁?
死锁(Deadlock) 指两个或多个线程互相等待对方释放资源,最终都无法推进执行的情况。
✅ 死锁产生的四个必要条件(同时满足才可能死锁):
🔹 互斥条件:资源每次只能被一个线程占用;
🔹 占有且等待:一个线程持有资源的同时,等待其它资源;
🔹 不可剥夺:资源不能被强制回收,只能释放后其他线程才可获得;
🔹 循环等待:两个或多个线程形成一种“资源环”。
✅ 死锁示例代码:
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) {
System.out.println("Thread-1 got lockA");
try { Thread.sleep(100); } catch (InterruptedException e) {}
synchronized (lockB) {
System.out.println("Thread-1 got lockB");
}
}
});
Thread t2 = new Thread(() -> {
synchronized (lockB) {
System.out.println("Thread-2 got lockB");
try { Thread.sleep(100); } catch (InterruptedException e) {}
synchronized (lockA) {
System.out.println("Thread-2 got lockA");
}
}
});
t1.start();
t2.start();
}
}
🔁 以上代码中,Thread-1
和 Thread-2
各自持有一把锁,等待对方释放另一把锁,结果导致死锁。
🧯 3、死锁的解决策略
✅ 3.1 避免资源的嵌套锁(破坏“占有且等待”)
统一加锁顺序,永远以固定的顺序获取多个锁。
public class DeadlockFree {
private static final Object lockA = new Object();
private static final Object lockB = new Object();
public void doSafeLock() {
Object first = lockA;
Object second = lockB;
synchronized (first) {
synchronized (second) {
System.out.println("Acquired both locks safely");
}
}
}
}
固定资源加锁顺序,避免循环等待。
✅ 3.2 使用 tryLock()
+ 超时机制(推荐)
使用 ReentrantLock.tryLock()
方法设置超时时间,一旦获取不到锁就放弃,从而避免死锁。
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.TimeUnit;
public class TryLockExample {
private final ReentrantLock lock1 = new ReentrantLock();
private final ReentrantLock lock2 = new ReentrantLock();
public void doWork() {
try {
if (lock1.tryLock(1, TimeUnit.SECONDS)) {
try {
if (lock2.tryLock(1, TimeUnit.SECONDS)) {
try {
System.out.println("Both locks acquired");
} finally {
lock2.unlock();
}
}
} finally {
lock1.unlock();
}
} else {
System.out.println("Could not acquire lock1");
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
🟢 实际业务中广泛应用在高并发交易、队列调度等系统中。
✅ 3.3 使用定时调度或死锁检测机制
如 Java Management API (ThreadMXBean
) 可用于检测死锁:
import java.lang.management.*;
public class DeadlockDetector {
public static void main(String[] args) throws InterruptedException {
ThreadMXBean mbean = ManagementFactory.getThreadMXBean();
while (true) {
long[] deadlockedThreads = mbean.findDeadlockedThreads();
if (deadlockedThreads != null) {
System.err.println("Deadlock detected!");
}
Thread.sleep(5000);
}
}
}
可用于线上服务定期探测死锁,报警或自动重启线程。
✅ 3.4 尽量减少锁的使用范围(粒度)
锁粗 → 粗暴但易死锁
锁细 → 复杂但更高效和安全
建议:
🔹 只锁住共享资源;
🔹 让锁尽快释放;
🔹 非必要不使用嵌套锁。
✅ 3.5 使用无锁并发工具(如 CAS、并发集合)
比如使用 ConcurrentHashMap
替代 Hashtable
,使用 AtomicInteger
替代 int + synchronized
。
🎯 5、总结
🔹 代码审查时关注锁的顺序和范围
🔹 高并发模块使用 tryLock 控制资源
🔹 定期检测死锁、记录线程堆栈信息
策略 | 优点 | 场景推荐 |
---|---|---|
固定锁顺序 | 简单有效 | 本地多线程 |
tryLock + timeout | 可控退出 | 高并发服务场景 |
死锁检测工具 | 可监控告警 | 线上部署 |
减少锁粒度 | 性能提升 | 性能敏感业务 |
无锁并发工具 | 高性能、无死锁 | 算法级操作 |