死锁解决策略
面试官问:"什么是死锁?如何解决死锁?"
小王说:"死锁就是两个线程互相等待对方释放资源...可以用银行家算法避免..."
面试官追问:"银行家算法需要什么前提?为什么实际系统很少用它?"
小王支支吾吾:"需要预先知道最大需求...因为..."
面试官又问:"那除了避免,还有什么策略?如果死锁已经发生了怎么办?"
小王彻底卡住了。
死锁是操作系统领域的经典难题,但很多人只了解皮毛。今天,我们把死锁的四大解决策略全部讲透。
一、从一个问题开始
先看一个经典的死锁场景——哲学家就餐问题:
这就是经典的死锁场景。每个哲学家都在等待右边的筷子,而右边的筷子正被左边的哲学家拿着。
死锁的四个必要条件( Coffman 条件):
【直观类比】
死锁 = 十字路口堵死
想象一个没有红绿灯的十字路口:
四大策略 = 四种应对堵车的方法
死锁 vs 活锁 vs 饥饿
二、核心原理
1. 死锁预防(Deadlock Prevention)
核心思想:破坏死锁的四个必要条件之一,从根本上杜绝死锁。
破坏互斥条件
破坏占有并等待条件
破坏不可抢占条件
破坏循环等待条件
┌─────────────────────────────────────────────────────┐
│ 银行家算法核心 │
├─────────────────────────────────────────────────────┤
│ │
│ 数据结构: │
│ - Available[j]:资源j的可用数量 │
│ - Max[i][j]:进程i对资源j的最大需求 │
│ - Allocation[i][j]:已分配给进程i的资源j数量 │
│ - Need[i][j]:进程i还需要的资源j数量 │
│ │
│ 验证公式: │
│ Need[i][j] = Max[i][j] - Allocation[i][j] │
│ │
│ 安全性检查: │
│ 1. 找 Need <= Available 的进程 │
│ 2. 假设该进程完成,释放其资源 │
│ 3. 重复直到所有进程都能完成 │
│ │
└─────────────────────────────────────────────────────┘
银行家算法的局限性:
资源分配图算法(简化版)
对于单实例资源类型:
3. 死锁检测(Deadlock Detection)
核心思想:允许死锁发生,但系统会定期检测是否发生了死锁。如果检测到死锁,就采取措施恢复。
检测时机
检测算法
4. 死锁恢复(Deadlock Recovery)
核心思想:检测到死锁后,采取措施解除死锁。
方法一:终止进程
方法二:资源抢占
三、边界与特例
1. 实际系统中的死锁处理
2. 分布式死锁
单机死锁和分布式死锁有很大区别:
3. 锁粒度与死锁风险
四、常见误区
❌ 误区一:死锁只会在代码里出现
死锁可以在多个层面发生:
❌ 误区二:避免比预防好
❌ 误区三:只要加锁就一定会死锁
死锁需要四个条件同时满足:
❌ 误区四:死锁检测可以完全解决问题
检测只是发现问题,不解决问题:
五、记忆技巧
一句话总结
死锁四条件要记牢:互斥、占有、不可抢、循环等。 预防破坏条件,避免动态检查,检测靠算法,恢复靠终止或抢占。
对比速记表
口诀
"死锁四个条件,缺一不可才能死" "预防破坏是根本,避免要算安全序" "检测周期要合理,恢复可杀可抢占" "加锁顺序要统一,超时机制是保险"
场景速查表
六、实战检验
自检题目
题目1:哲学家就餐问题有哪些解决方案?
点击查看答案
-
最多允许N-1个哲学家同时入座
- 破坏循环等待条件
- 5根筷子最多4个人同时吃
-
奇数号先拿左边,偶数号先拿右边
- 破坏循环等待条件
- 不会有环等待
-
拿不到右边就放下左边
- 破坏占有并等待条件
- 引入活锁风险(需要随机等待)
-
服务员协调(引入一个中心协调者)
- 相当于银行家算法
- 服务员判断能否让哲学家吃饭
-
检测+恢复
- 检测到死锁后,让某个哲学家放下筷子
题目2:为什么实际系统中更常用死锁检测而不是避免?
点击查看答案
-
避免需要预先知道最大需求
- 实际系统很难准确预估
- 动态变化的场景无法使用银行家算法
-
避免开销大
- 每次资源分配都要做安全性检查
- O(m × n²) 的时间复杂度
-
检测可以定期进行
- 不影响正常资源分配
- 在系统空闲时再检测
-
检测+恢复更灵活
- 可以选择代价最小的恢复方案
- 根据实际情况决定是否值得回滚
题目3:如何在代码中预防死锁?
点击查看答案
- 固定加锁顺序
- 使用超时机制
- 减少锁的持有时间
- 只在必要时加锁
- 锁外的操作尽量多
- 避免在持有锁时调用外部方法
- 外部方法可能获取其他锁
- 外部方法可能阻塞
- 使用Lock的高级特性
- ReentrantReadWriteLock 读写分离
- StampedLock 乐观读
面试追问预测
七、生产避坑指南
Java并发中的死锁检测
数据库死锁处理
死锁恢复时,如果选择不当的事务回滚,可能导致业务数据不一致。建议在事务设计时就考虑好回滚的代价和影响。