一、死锁的产生原因
死锁是多个线程(或进程)因竞争资源而陷入无限等待的状态,需同时满足以下 四个须要条件:
- 互斥条件(Mutual Exclusion)
- 资源一次只能被一个线程独占使用(如锁、文件句柄)。
- 请求与保持(Hold and Wait)
- 线程在持有至少一个资源的同时,请求其他线程占有的资源。
- 不可剥夺(No Preemption)
- 循环等待(Circular Wait)
- 多个线程形成环形等待链,每个线程都在等待下一个线程释放资源。
二、典型死锁场景示例
Java 代码示例:
- public class DeadlockDemo {
- private static final Object lockA = new Object();
- private static final Object lockB = new Object();
- public static void main(String[] args) {
- new Thread(() -> {
- synchronized (lockA) {
- System.out.println("Thread1 holds lockA");
- try { Thread.sleep(100); } catch (InterruptedException e) {}
- synchronized (lockB) { // 等待Thread2释放lockB
- System.out.println("Thread1 holds lockB");
- }
- }
- }).start();
- new Thread(() -> {
- synchronized (lockB) {
- System.out.println("Thread2 holds lockB");
- try { Thread.sleep(100); } catch (InterruptedException e) {}
- synchronized (lockA) { // 等待Thread1释放lockA
- System.out.println("Thread2 holds lockA");
- }
- }
- }).start();
- }
- }
复制代码 结果:
两个线程互相等待对方释放锁,程序无限卡死。
三、死锁的检测与诊断
1. 使用工具检测死锁
- jstack(Java自带工具):
- jstack <pid> # 输出线程快照,显示死锁的线程及持有/等待的锁
复制代码 - VisualVM 或 JConsole:
图形化界面查看线程状态,直接标记死锁。
2. 日记分析
若日记中线程长时间处于 BLOCKED 状态且无进展,大概发存亡锁。
四、死锁的避免战略
1. 破坏“请求与保持”条件
- 一次性申请全部资源:
线程在开始执行前申请全部所需资源,否则不执行。
缺点:资源利用率低,大概导致饥饿。
2. 破坏“不可剥夺”条件
- 答应抢占资源:
若线程请求资源失败,强制释放已持有的资源(需支持回滚操纵)。
缺点:实现复杂,适用于特定场景(如数据库事务)。
3. 破坏“循环等待”条件
- 资源有序分配法:
为全部资源类型定义全局次序,线程按次序申请资源。
示例:
规定必须先申请 lockA 再申请 lockB,避免交叉申请。
- // 修改后代码:两个线程均按 lockA → lockB 顺序申请
- new Thread(() -> {
- synchronized (lockA) {
- synchronized (lockB) { /* 逻辑 */ }
- }
- }).start();
- new Thread(() -> {
- synchronized (lockA) {
- synchronized (lockB) { /* 逻辑 */ }
- }
- }).start();
复制代码 4. 使用超时机制
- 实验获取锁时设置超时:
若在指定时间内未获得锁,放弃并释放已持有的资源,避免无限等待。
Java实现(使用 ReentrantLock):- Lock lockA = new ReentrantLock();
- Lock lockB = new ReentrantLock();
- if (lockA.tryLock(1, TimeUnit.SECONDS)) {
- try {
- if (lockB.tryLock(1, TimeUnit.SECONDS)) {
- try { /* 逻辑 */ }
- finally { lockB.unlock(); }
- }
- } finally { lockA.unlock(); }
- }
复制代码 5. 镌汰锁的粒度
- 缩小同步范围:
仅对须要代码加锁,镌汰锁的持有时间。
- 使用线程安全的数据结构:
如 ConcurrentHashMap 替代 synchronized + HashMap。
五、最佳实践总结
战略适用场景长处缺点资源有序分配多锁交叉申请场景简朴有用,预防循环等待需全局统一次序,大概限制灵活性超时机制高并发、答应重试的场景避免无限等待,提升体系结实性需处理超时重试逻辑无锁编程(CAS、原子类)低竞争、简朴操纵场景高性能,无死锁风险复杂逻辑实现困难事务回滚数据库、支持回滚的操纵保证数据同等性实现资本高 六、总结
死锁的避免需联合业务场景选择符合的战略:
- 关键体系(如金融交易):优先使用资源有序分配和超时机制。
- 高并发体系:镌汰锁粒度,采用无锁数据结构。
- 复杂事务:联合事务管理和回滚机制。
通过代码规范、工具检测和设计优化,可显著降低死锁发生概率。
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |