死锁
死锁是执行主体相互等待资源,导致无法继续的状态。本文负责死锁条件、预防、避免和恢复。
1. 面试核心
先记住这几条,再读正文2. 等待环
线程 T1:持有 A,等待 B
线程 T2:持有 B,等待 A
A、B 各只能由一个线程持有。两个线程都等到获得另一把锁才释放原锁。它们形成 T1 → T2 → T1 的等待环。
| 必要条件 | 含义 |
|---|---|
| 互斥 | 同一资源实例不能同时分配给多个主体。 |
| 占有并等待 | 持有资源时继续等待其他资源。 |
| 不可剥夺 | 资源由持有者主动释放。 |
| 循环等待 | 每个主体等待环中下一个主体持有的资源。 |
每类资源仅有一个实例时,等待图中的环意味着死锁。多实例资源图中的环则只提示死锁风险。其他实例的释放仍能解除等待。
3. 预防与恢复
预防通过破坏必要条件阻止死锁。例如所有线程都先获取 A,再获取 B。多个业务对象应按稳定键排序后加锁。
一次性取得所需资源可破坏占有并等待。尝试获取失败时,应释放已有资源再重试。该方案会增加等待或资源闲置。
检测机制收集等待关系,判断是否存在死锁。恢复可中止任务或回滚操作,再释放资源。强制停止持锁线程前,必须能恢复共享状态。
4. 安全状态与死锁避免
安全状态存在一种任务完成顺序。系统按该顺序分配资源,所有任务都能完成。银行家算法在批准新请求前检查这一条件。
假设共 10 份同类资源。P1 已有 5 份,最多需要 7 份。P2 已有 3 份,最多需要 9 份。剩余 2 份可以先给 P1。P1 完成后释放 7 份,足够 P2 完成。
若先把剩余 2 份给 P2,就没有可用资源。两者都无法获得声明的剩余最大需求。该分配会进入不安全状态,应被拒绝。
不安全状态表示缺少保证完成的安全序列。若实际需求低于声明上限,任务仍能结束。因此不安全状态与已经死锁需分别判断。
银行家算法依赖事先已知的最大需求。通用服务难以准确预报需求,锁顺序更易落地。
5. 使用边界
长等待也会来自慢 I/O 或资源不足。诊断时需要线程栈、持有者和完整等待链。
活锁中线程持续运行,却因相互干扰而缺少进展。饥饿则是某个任务长期得不到资源。退避和随机抖动可减少重试碰撞。公平排队可改善饥饿。
锁超时后只重试当前调用,会保留原有等待条件。失败路径应释放资源,再开启新的尝试。
6 · 面试题卡片
本页题目与参考回答
6.1 死锁需要哪些条件,如何避免两个线程反向获取锁造成的死锁?
考察重点:经典死锁条件、等待环与统一加锁顺序的作用。
参考回答
经典死锁需要四个条件同时成立。
它们是互斥、占有并等待、不可剥夺和循环等待。让所有线程按统一顺序获取 A、B,可打破循环等待。使用超时时,还需释放已持有资源并恢复中间状态。
6.2 死锁预防和避免有什么区别,不安全状态就是死锁吗?
考察重点:必要条件、安全序列与需求上限。
参考回答
预防通过规则破坏某个必要条件。
避免则在每次分配时检查安全状态。银行家算法依赖任务声明的最大资源需求。不安全状态缺少安全完成保证,但实际需求更小时仍可结束。