副标题 / 摘要

死锁发生在多个线程互相等待对方持有的资源。本文解释成因、给出示例,并总结规避方法。

目标读者

  • 需要理解并发风险的工程师
  • 负责系统可靠性的开发者
  • 做并发设计评审的技术负责人

背景 / 动机

死锁通常在压力或线上才出现,一旦发生会导致服务完全卡住。
理解死锁条件并提前规避,是并发系统的必修课。

核心概念

  • 互斥:资源一次只能被一个线程占用
  • 占有且等待:拿着资源还要等另一个资源
  • 不可抢占:资源不能被强制夺取
  • 循环等待:形成等待环

实践指南 / 步骤

  1. 统一锁顺序(按固定顺序获取)
  2. 减少锁数量
  3. 设置超时与回退
  4. 用锁分层减少交叉等待
  5. 监控阻塞与持锁时间

可运行示例

import threading
import time

lock_a = threading.Lock()
lock_b = threading.Lock()


def t1():
    with lock_a:
        time.sleep(0.1)
        with lock_b:
            pass


def t2():
    with lock_b:
        time.sleep(0.1)
        with lock_a:
            pass


if __name__ == "__main__":
    threading.Thread(target=t1).start()
    threading.Thread(target=t2).start()
    time.sleep(1)
    print("可能已死锁")

解释与原理

当 t1 拿着 A 等 B,而 t2 拿着 B 等 A,就形成循环等待。
满足四个必要条件时死锁发生。

常见问题与注意事项

  1. 锁顺序能完全避免死锁吗?
    在固定顺序下可以显著降低风险。

  2. 超时锁有什么用?
    可避免无限等待,进行回退或重试。

  3. 死锁与饥饿的区别?
    死锁是互相等待,饥饿是一直等不到资源。

最佳实践与建议

  • 统一锁顺序是最有效的策略
  • 避免嵌套锁或减少锁粒度
  • 用监控发现长时间持锁

小结 / 结论

死锁不是偶然,而是设计失误导致。
通过锁顺序、超时与结构化并发,可以大幅减少死锁风险。

参考与延伸阅读

  • The Art of Multiprocessor Programming
  • Java Concurrency in Practice
  • Go/Java/Python 并发锁实践

元信息

  • 阅读时长:7~9 分钟
  • 标签:死锁、并发、线程安全
  • SEO 关键词:Deadlock, 死锁, 锁顺序
  • 元描述:解释死锁成因并给出规避策略。

行动号召(CTA)

检查一次你的锁获取顺序,找出是否存在潜在的循环等待。