死锁(Deadlock)是并发系统中多个进程/线程因互相持有对方所需资源且不释放,导致全体永久阻塞、无法继续推进的状态。
四个必要条件(Coffman 条件)
必须同时满足才会死锁,破任一即可避免:
- 互斥:资源只能被一个占用(如锁、打印机)
- 占有且等待:持有一资源的同时等待另一资源
- 不可抢占:已分配资源不能被强行夺走,只能主动释放
- 循环等待:A等B、B等C、C等A,形成环
常见场景
- 数据库:事务1锁表A等表B,事务2锁表B等表A
- 多线程:线程1
lock(a); lock(b),线程2lock(b); lock(a) - 分布式:两个服务互相 RPC 调用且都加了本地锁
处理方式对比
| 策略 | 做法 | 优缺点 |
|---|---|---|
| 预防 | 破坏四条件之一(如按固定顺序加锁、一次性申请所有资源) | 简单,但可能降低并发度 |
| 避免 | 银行家算法,分配前判断是否会进入不安全状态 | 理论完美,实际难获取未来需求 |
| 检测+恢复 | 定期建资源分配图找环,发现后回滚/终止进程 | 允许死锁发生,开销在检测与恢复 |
| 忽略 | 假装不存在,重启解决(多数操作系统/业务系统的现实选择) | 成本最低,适合死锁概率极低的场景 |
工程级避坑要点
- 统一加锁顺序:所有地方都按
a → b → c顺序拿锁,循环等待不成立 - 加超时:
tryLock(timeout),拿不到就回退并释放已持锁 - 减小锁粒度:用读写锁、分段锁,别长事务占着锁做 IO
- 数据库层:短事务、避免用户交互中间留事务、FK 索引齐全防隐式锁升级
- 工具:Java 用
jstack看BLOCKED链;MySQL 查information_schema.INNODB_TRX+SHOW ENGINE INNODB STATUS