死锁

返回计算机基础

死锁(Deadlock)是指多个进程/线程因互相等待对方持有的资源而永久阻塞的状态。


四个必要条件(Coffman 条件)

死锁发生当且仅当以下四个条件同时成立

条件说明
互斥资源一次只能被一个进程使用
占有并等待进程持有资源的同时等待其他资源
不可抢占资源只能由持有者主动释放,不能被强制剥夺
循环等待存在进程等待链形成环路:P1 等 P2,P2 等 P3,…,Pn 等 P1

破坏任意一个条件即可预防死锁。


资源分配图(RAG)

用有向图表示进程与资源的分配/请求关系:

P1 ──请求──→ R1
R1 ──分配──→ P2
P2 ──请求──→ R2
R2 ──分配──→ P1

上图形成环路 → 死锁

图中存在环路是死锁的必要条件(每种资源只有一个实例时也是充分条件)。


处理策略

1. 死锁预防(Prevention)

静态策略,破坏四个条件之一:

破坏条件做法代价
互斥尽量使用无锁结构不适用所有资源
占有并等待一次性申请所有资源,或申请新资源前释放已有资源资源利用率低
不可抢占允许强制剥夺资源(如 CPU 时间片)需保存/恢复状态
循环等待对资源编号,进程只能按序申请限制申请灵活性

2. 死锁避免(Avoidance)

动态策略,分配前判断是否会导致不安全状态。

银行家算法:每次分配前检查是否存在安全序列(按该顺序分配,所有进程都能完成):

安全状态 → 存在安全序列 → 不会死锁
不安全状态 → 可能死锁(拒绝本次分配)

示例(3 个进程,共 10 个资源):

进程最大需求已分配还需
P0707
P1321
P2936

剩余可用:5。安全序列:P1(需 1≤5,完成后可用 7)→ P0(需 7≤7)→ P2(需 6≤9)✓

3. 死锁检测与恢复(Detection & Recovery)

允许死锁发生,定期检测,发现后恢复:

检测:定期运行资源分配图的环路检测(DFS)。

恢复方式

方式说明
进程终止终止死锁环中的一个或所有进程
资源抢占强制剥夺某进程的资源,将进程回滚到安全状态

选择目标的原则:优先级最低、已运行时间最短、占用资源最多。

4. 鸵鸟策略(Ostrich Algorithm)

假装死锁不会发生,出现时重启系统。适用于死锁概率极低的系统(如大多数桌面 OS)。


实际场景

数据库死锁

两个事务互相持有对方需要的行锁:

T1:lock(Row A) → 等待 lock(Row B)
T2:lock(Row B) → 等待 lock(Row A)

数据库通过超时等待图检测发现死锁,回滚代价较小的事务。

Java 死锁示例

// 线程 1
synchronized(lockA) {
    synchronized(lockB) { ... }
}
// 线程 2
synchronized(lockB) {
    synchronized(lockA) { ... }  // 循环等待
}

预防:统一锁顺序(始终先锁 A 再锁 B);使用 tryLock(timeout) 替代 lock()


活锁与饥饿

现象说明
死锁进程永久阻塞,无法推进
活锁进程不断改变状态但无法推进(互相”礼让”)
饥饿进程长期得不到资源(低优先级持续被抢占)

相关链接