Skip to content

deadlock-and-thread-dump ​

1. 实验目标与工程要点 ​

本实验构造一个确定性双锁死锁:两个线程以相反顺序获取两把锁,必然互相等待对方已持有的锁,从而永久阻塞。目标是让你亲手制造死锁,并学会用线程 dump 定位它——这是线上「卡死类」问题的标准排查手段。

死锁形成的必要条件(四个同时满足才成立) ​

条件含义本实验如何满足
互斥资源同一时刻只能被一个线程持有synchronized 锁天然互斥
持有并等待持有一把锁的同时等待另一把T1 持 A 等 B;T2 持 B 等 A
不可剥夺锁只能由持有者主动释放synchronized 不会强制剥夺
循环等待线程间形成等待环路T1→等 B(被 T2 持)→ T2→等 A(被 T1 持)

重点观察 ​

  • jstack 输出里的 BLOCKED 状态与 "Found one Java-level deadlock" 自检信息。
  • 锁拥有者链路:T1 owns lockA waiting for lockB;T2 owns lockB waiting for lockA。

2. 深入原理与生活浅显比喻 ​

2.1 交叉路口比喻 ​

两辆车(T1、T2)分别从左、从右驶入一个单车道窄桥(两把锁 = 桥两段):

  • T1 占了桥左段(lockA),想往前开进桥右段(lockB);
  • T2 占了桥右段(lockB),想退回到桥左段(lockA);
  • 谁都不肯倒车(锁不可剥夺),于是两辆车在桥中央僵死。

这对应死锁四条件的「循环等待」。要打破它,最简单的是统一加锁顺序:大家都先申请左段再申请右段,环路就不成立了。

2.2 为什么 sleep(500) 能让死锁「稳定复现」 ​

源码里每个线程拿到第一把锁后 sleep(500),目的不是制造死锁(顺序相反本身就够),而是大幅提高两个线程都先拿到第一把锁的概率——否则随机时序下可能其中一个线程连续拿到两把锁就跑掉了,死锁看不到。教学 demo 要可复现,所以人为拉开时间窗。


3. 动手步骤 ​

3.1 运行死锁程序 ​

bash
cd labs/java/runtime-concurrency/deadlock-and-thread-dump
javac -d out src/DeadlockDemo.java
java -cp out deadlock.DeadlockDemo &

程序会卡住(不会打印「正常结束」),此时另开一个终端:

3.2 抓取线程 dump ​

bash
# 方式一:jstack(需 PID)
jps            # 找到 DeadlockDemo 的 PID
jstack <PID>

# 方式二:向进程发 QUIT 信号(类 Unix)
kill -3 <PID>  # 线程 dump 直接打印到标准输出

3.3 怎么读 dump(关键片段) ​

text
Found one Java-level deadlock:
=============================
"Deadlock-T2":
  waiting to lock monitor 0x... (object 0x..., a java.lang.Object)
  which is held by "Deadlock-T1"
"Deadlock-T1":
  waiting to lock monitor 0x... (object 0x..., a java.lang.Object)
  which is held by "Deadlock-T2"

Java stack information for the threads listed above:
"Deadlock-T1":
  at deadlock.DeadlockDemo.lambda$main$0(DeadlockDemo.java:..)
  - waiting to lock <0x...> (a java.lang.Object)
  - locked <0x...> (a java.lang.Object)   <-- 已持有 lockA
"Deadlock-T2":
  at deadlock.DeadlockDemo.lambda$main$1(DeadlockDemo.java:..)
  - waiting to lock <0x...> (a java.lang.Object)
  - locked <0x...> (a java.lang.Object)   <-- 已持有 lockB

要点:waiting to lock 与 locked 同现,且两线程的锁互相是对方的 locked,就是死锁的铁证。配合 BLOCKED 状态、Deadlock-T1/Deadlock-T2 的持锁-等待关系,可以一路反推到代码中先拿哪把、后拿哪把。

3.4 如何修复(留作练习) ​

把 T2 也改成「先 lockA 再 lockB」(与 T1 同序),循环等待消失,死锁解除。这正是模块 08 里「锁顺序固定」原则的来源。


4. 源码 ​


5. 对应知识库文档 ​

站点构建时间:2026/8/24 23:43:17