死锁题很容易被答成一句话:两个线程互相等待。
这句话当然对,但面试里不够。更完整的回答应该包括三层:
- 死锁怎么写出来的。
- 死锁成立需要哪些条件。
- 线上或本地怎么定位。
一个最典型的死锁
两个线程分别持有一把锁,又去等待对方手里的锁。
java
Object A = new Object();
Object B = new Object();
Thread t1 = new Thread(() -> {
synchronized (A) {
System.out.println("lock A");
sleep(1000);
synchronized (B) {
System.out.println("lock B");
}
}
}, "t1");
Thread t2 = new Thread(() -> {
synchronized (B) {
System.out.println("lock B");
sleep(500);
synchronized (A) {
System.out.println("lock A");
}
}
}, "t2");
t1.start();
t2.start();
执行到某个时刻:
线程 t1 持有锁 A,等待锁 B。
线程 t2 持有锁 B,等待锁 A。
谁都不肯放,谁都往下走不了。
线程 t2 锁 B 锁 A 线程 t1 线程 t2 锁 B 锁 A 线程 t1 #mermaid-svg-88Q2bmUulRBpXVF7{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-88Q2bmUulRBpXVF7 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-88Q2bmUulRBpXVF7 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-88Q2bmUulRBpXVF7 .error-icon{fill:#552222;}#mermaid-svg-88Q2bmUulRBpXVF7 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-88Q2bmUulRBpXVF7 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-88Q2bmUulRBpXVF7 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-88Q2bmUulRBpXVF7 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-88Q2bmUulRBpXVF7 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-88Q2bmUulRBpXVF7 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-88Q2bmUulRBpXVF7 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-88Q2bmUulRBpXVF7 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-88Q2bmUulRBpXVF7 .marker.cross{stroke:#333333;}#mermaid-svg-88Q2bmUulRBpXVF7 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-88Q2bmUulRBpXVF7 p{margin:0;}#mermaid-svg-88Q2bmUulRBpXVF7 .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-88Q2bmUulRBpXVF7 text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-88Q2bmUulRBpXVF7 .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-88Q2bmUulRBpXVF7 .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-88Q2bmUulRBpXVF7 .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-88Q2bmUulRBpXVF7 .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-88Q2bmUulRBpXVF7 #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-88Q2bmUulRBpXVF7 .sequenceNumber{fill:white;}#mermaid-svg-88Q2bmUulRBpXVF7 #sequencenumber{fill:#333;}#mermaid-svg-88Q2bmUulRBpXVF7 #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-88Q2bmUulRBpXVF7 .messageText{fill:#333;stroke:none;}#mermaid-svg-88Q2bmUulRBpXVF7 .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-88Q2bmUulRBpXVF7 .labelText,#mermaid-svg-88Q2bmUulRBpXVF7 .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-88Q2bmUulRBpXVF7 .loopText,#mermaid-svg-88Q2bmUulRBpXVF7 .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-88Q2bmUulRBpXVF7 .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-88Q2bmUulRBpXVF7 .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-88Q2bmUulRBpXVF7 .noteText,#mermaid-svg-88Q2bmUulRBpXVF7 .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-88Q2bmUulRBpXVF7 .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-88Q2bmUulRBpXVF7 .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-88Q2bmUulRBpXVF7 .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-88Q2bmUulRBpXVF7 .actorPopupMenu{position:absolute;}#mermaid-svg-88Q2bmUulRBpXVF7 .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-88Q2bmUulRBpXVF7 .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-88Q2bmUulRBpXVF7 .actor-man circle,#mermaid-svg-88Q2bmUulRBpXVF7 line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-88Q2bmUulRBpXVF7 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 互相等待,程序无法继续 获取锁 A获取锁 B等待锁 B等待锁 A
死锁的四个必要条件
经典死锁有四个必要条件:
| 条件 | 含义 |
|---|---|
| 互斥条件 | 资源同一时刻只能被一个线程占有 |
| 占有且等待 | 线程持有资源的同时,还在等待其他资源 |
| 不可抢占 | 资源不能被外部强行剥夺,只能由持有者释放 |
| 循环等待 | 多个线程形成首尾相接的等待环 |
#mermaid-svg-usCPk6WmBpcPb2wB{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-usCPk6WmBpcPb2wB .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-usCPk6WmBpcPb2wB .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-usCPk6WmBpcPb2wB .error-icon{fill:#552222;}#mermaid-svg-usCPk6WmBpcPb2wB .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-usCPk6WmBpcPb2wB .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-usCPk6WmBpcPb2wB .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-usCPk6WmBpcPb2wB .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-usCPk6WmBpcPb2wB .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-usCPk6WmBpcPb2wB .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-usCPk6WmBpcPb2wB .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-usCPk6WmBpcPb2wB .marker{fill:#333333;stroke:#333333;}#mermaid-svg-usCPk6WmBpcPb2wB .marker.cross{stroke:#333333;}#mermaid-svg-usCPk6WmBpcPb2wB svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-usCPk6WmBpcPb2wB p{margin:0;}#mermaid-svg-usCPk6WmBpcPb2wB .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-usCPk6WmBpcPb2wB .cluster-label text{fill:#333;}#mermaid-svg-usCPk6WmBpcPb2wB .cluster-label span{color:#333;}#mermaid-svg-usCPk6WmBpcPb2wB .cluster-label span p{background-color:transparent;}#mermaid-svg-usCPk6WmBpcPb2wB .label text,#mermaid-svg-usCPk6WmBpcPb2wB span{fill:#333;color:#333;}#mermaid-svg-usCPk6WmBpcPb2wB .node rect,#mermaid-svg-usCPk6WmBpcPb2wB .node circle,#mermaid-svg-usCPk6WmBpcPb2wB .node ellipse,#mermaid-svg-usCPk6WmBpcPb2wB .node polygon,#mermaid-svg-usCPk6WmBpcPb2wB .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-usCPk6WmBpcPb2wB .rough-node .label text,#mermaid-svg-usCPk6WmBpcPb2wB .node .label text,#mermaid-svg-usCPk6WmBpcPb2wB .image-shape .label,#mermaid-svg-usCPk6WmBpcPb2wB .icon-shape .label{text-anchor:middle;}#mermaid-svg-usCPk6WmBpcPb2wB .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-usCPk6WmBpcPb2wB .rough-node .label,#mermaid-svg-usCPk6WmBpcPb2wB .node .label,#mermaid-svg-usCPk6WmBpcPb2wB .image-shape .label,#mermaid-svg-usCPk6WmBpcPb2wB .icon-shape .label{text-align:center;}#mermaid-svg-usCPk6WmBpcPb2wB .node.clickable{cursor:pointer;}#mermaid-svg-usCPk6WmBpcPb2wB .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-usCPk6WmBpcPb2wB .arrowheadPath{fill:#333333;}#mermaid-svg-usCPk6WmBpcPb2wB .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-usCPk6WmBpcPb2wB .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-usCPk6WmBpcPb2wB .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-usCPk6WmBpcPb2wB .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-usCPk6WmBpcPb2wB .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-usCPk6WmBpcPb2wB .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-usCPk6WmBpcPb2wB .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-usCPk6WmBpcPb2wB .cluster text{fill:#333;}#mermaid-svg-usCPk6WmBpcPb2wB .cluster span{color:#333;}#mermaid-svg-usCPk6WmBpcPb2wB div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-usCPk6WmBpcPb2wB .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-usCPk6WmBpcPb2wB rect.text{fill:none;stroke-width:0;}#mermaid-svg-usCPk6WmBpcPb2wB .icon-shape,#mermaid-svg-usCPk6WmBpcPb2wB .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-usCPk6WmBpcPb2wB .icon-shape p,#mermaid-svg-usCPk6WmBpcPb2wB .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-usCPk6WmBpcPb2wB .icon-shape .label rect,#mermaid-svg-usCPk6WmBpcPb2wB .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-usCPk6WmBpcPb2wB .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-usCPk6WmBpcPb2wB .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-usCPk6WmBpcPb2wB :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 死锁成立
互斥
占有且等待
不可抢占
循环等待
锁一次只能一个线程持有
拿着 A 等 B
别人不能强行夺走 A
t1 等 t2, t2 等 t1
预防死锁,本质就是破坏其中一个条件。工程上最常见的是破坏循环等待:固定加锁顺序。
比如所有线程都先拿 A,再拿 B,就不会出现一个拿 A 等 B、另一个拿 B 等 A。
#mermaid-svg-qZbnZ8qjefNoNzKm{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-qZbnZ8qjefNoNzKm .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-qZbnZ8qjefNoNzKm .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-qZbnZ8qjefNoNzKm .error-icon{fill:#552222;}#mermaid-svg-qZbnZ8qjefNoNzKm .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-qZbnZ8qjefNoNzKm .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-qZbnZ8qjefNoNzKm .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-qZbnZ8qjefNoNzKm .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-qZbnZ8qjefNoNzKm .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-qZbnZ8qjefNoNzKm .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-qZbnZ8qjefNoNzKm .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-qZbnZ8qjefNoNzKm .marker{fill:#333333;stroke:#333333;}#mermaid-svg-qZbnZ8qjefNoNzKm .marker.cross{stroke:#333333;}#mermaid-svg-qZbnZ8qjefNoNzKm svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-qZbnZ8qjefNoNzKm p{margin:0;}#mermaid-svg-qZbnZ8qjefNoNzKm .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-qZbnZ8qjefNoNzKm .cluster-label text{fill:#333;}#mermaid-svg-qZbnZ8qjefNoNzKm .cluster-label span{color:#333;}#mermaid-svg-qZbnZ8qjefNoNzKm .cluster-label span p{background-color:transparent;}#mermaid-svg-qZbnZ8qjefNoNzKm .label text,#mermaid-svg-qZbnZ8qjefNoNzKm span{fill:#333;color:#333;}#mermaid-svg-qZbnZ8qjefNoNzKm .node rect,#mermaid-svg-qZbnZ8qjefNoNzKm .node circle,#mermaid-svg-qZbnZ8qjefNoNzKm .node ellipse,#mermaid-svg-qZbnZ8qjefNoNzKm .node polygon,#mermaid-svg-qZbnZ8qjefNoNzKm .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-qZbnZ8qjefNoNzKm .rough-node .label text,#mermaid-svg-qZbnZ8qjefNoNzKm .node .label text,#mermaid-svg-qZbnZ8qjefNoNzKm .image-shape .label,#mermaid-svg-qZbnZ8qjefNoNzKm .icon-shape .label{text-anchor:middle;}#mermaid-svg-qZbnZ8qjefNoNzKm .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-qZbnZ8qjefNoNzKm .rough-node .label,#mermaid-svg-qZbnZ8qjefNoNzKm .node .label,#mermaid-svg-qZbnZ8qjefNoNzKm .image-shape .label,#mermaid-svg-qZbnZ8qjefNoNzKm .icon-shape .label{text-align:center;}#mermaid-svg-qZbnZ8qjefNoNzKm .node.clickable{cursor:pointer;}#mermaid-svg-qZbnZ8qjefNoNzKm .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-qZbnZ8qjefNoNzKm .arrowheadPath{fill:#333333;}#mermaid-svg-qZbnZ8qjefNoNzKm .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-qZbnZ8qjefNoNzKm .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-qZbnZ8qjefNoNzKm .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-qZbnZ8qjefNoNzKm .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-qZbnZ8qjefNoNzKm .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-qZbnZ8qjefNoNzKm .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-qZbnZ8qjefNoNzKm .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-qZbnZ8qjefNoNzKm .cluster text{fill:#333;}#mermaid-svg-qZbnZ8qjefNoNzKm .cluster span{color:#333;}#mermaid-svg-qZbnZ8qjefNoNzKm div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-qZbnZ8qjefNoNzKm .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-qZbnZ8qjefNoNzKm rect.text{fill:none;stroke-width:0;}#mermaid-svg-qZbnZ8qjefNoNzKm .icon-shape,#mermaid-svg-qZbnZ8qjefNoNzKm .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-qZbnZ8qjefNoNzKm .icon-shape p,#mermaid-svg-qZbnZ8qjefNoNzKm .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-qZbnZ8qjefNoNzKm .icon-shape .label rect,#mermaid-svg-qZbnZ8qjefNoNzKm .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-qZbnZ8qjefNoNzKm .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-qZbnZ8qjefNoNzKm .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-qZbnZ8qjefNoNzKm :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 线程 t1
先获取锁 A
线程 t2
再获取锁 B
执行临界区
怎么用 jps 和 jstack 诊断
当 Java 程序疑似卡住,可以先用 jps 找进程。
bash
jps -l
找到目标进程 ID 后,用 jstack 打线程栈:
bash
jstack -l <pid>
如果确实发生死锁,jstack 通常会在输出里给出类似信息:
text
Found one Java-level deadlock:
并列出哪些线程持有哪些锁、正在等待哪些锁。
诊断链路可以这样看:
#mermaid-svg-wxnBKyrJAfVyvTqF{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-wxnBKyrJAfVyvTqF .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-wxnBKyrJAfVyvTqF .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-wxnBKyrJAfVyvTqF .error-icon{fill:#552222;}#mermaid-svg-wxnBKyrJAfVyvTqF .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-wxnBKyrJAfVyvTqF .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-wxnBKyrJAfVyvTqF .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-wxnBKyrJAfVyvTqF .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-wxnBKyrJAfVyvTqF .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-wxnBKyrJAfVyvTqF .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-wxnBKyrJAfVyvTqF .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-wxnBKyrJAfVyvTqF .marker{fill:#333333;stroke:#333333;}#mermaid-svg-wxnBKyrJAfVyvTqF .marker.cross{stroke:#333333;}#mermaid-svg-wxnBKyrJAfVyvTqF svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-wxnBKyrJAfVyvTqF p{margin:0;}#mermaid-svg-wxnBKyrJAfVyvTqF .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-wxnBKyrJAfVyvTqF .cluster-label text{fill:#333;}#mermaid-svg-wxnBKyrJAfVyvTqF .cluster-label span{color:#333;}#mermaid-svg-wxnBKyrJAfVyvTqF .cluster-label span p{background-color:transparent;}#mermaid-svg-wxnBKyrJAfVyvTqF .label text,#mermaid-svg-wxnBKyrJAfVyvTqF span{fill:#333;color:#333;}#mermaid-svg-wxnBKyrJAfVyvTqF .node rect,#mermaid-svg-wxnBKyrJAfVyvTqF .node circle,#mermaid-svg-wxnBKyrJAfVyvTqF .node ellipse,#mermaid-svg-wxnBKyrJAfVyvTqF .node polygon,#mermaid-svg-wxnBKyrJAfVyvTqF .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-wxnBKyrJAfVyvTqF .rough-node .label text,#mermaid-svg-wxnBKyrJAfVyvTqF .node .label text,#mermaid-svg-wxnBKyrJAfVyvTqF .image-shape .label,#mermaid-svg-wxnBKyrJAfVyvTqF .icon-shape .label{text-anchor:middle;}#mermaid-svg-wxnBKyrJAfVyvTqF .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-wxnBKyrJAfVyvTqF .rough-node .label,#mermaid-svg-wxnBKyrJAfVyvTqF .node .label,#mermaid-svg-wxnBKyrJAfVyvTqF .image-shape .label,#mermaid-svg-wxnBKyrJAfVyvTqF .icon-shape .label{text-align:center;}#mermaid-svg-wxnBKyrJAfVyvTqF .node.clickable{cursor:pointer;}#mermaid-svg-wxnBKyrJAfVyvTqF .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-wxnBKyrJAfVyvTqF .arrowheadPath{fill:#333333;}#mermaid-svg-wxnBKyrJAfVyvTqF .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-wxnBKyrJAfVyvTqF .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-wxnBKyrJAfVyvTqF .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-wxnBKyrJAfVyvTqF .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-wxnBKyrJAfVyvTqF .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-wxnBKyrJAfVyvTqF .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-wxnBKyrJAfVyvTqF .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-wxnBKyrJAfVyvTqF .cluster text{fill:#333;}#mermaid-svg-wxnBKyrJAfVyvTqF .cluster span{color:#333;}#mermaid-svg-wxnBKyrJAfVyvTqF div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-wxnBKyrJAfVyvTqF .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-wxnBKyrJAfVyvTqF rect.text{fill:none;stroke-width:0;}#mermaid-svg-wxnBKyrJAfVyvTqF .icon-shape,#mermaid-svg-wxnBKyrJAfVyvTqF .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-wxnBKyrJAfVyvTqF .icon-shape p,#mermaid-svg-wxnBKyrJAfVyvTqF .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-wxnBKyrJAfVyvTqF .icon-shape .label rect,#mermaid-svg-wxnBKyrJAfVyvTqF .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-wxnBKyrJAfVyvTqF .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-wxnBKyrJAfVyvTqF .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-wxnBKyrJAfVyvTqF :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 是
否
程序卡死 / 请求无响应
jps -l 找 Java 进程
jstack -l pid 导出线程栈
是否发现 deadlock
查看线程名、锁对象、代码行
回到代码分析加锁顺序
继续排查 BLOCKED / WAITING / IO 等问题
jstack 的价值不只是告诉你"有死锁"。更关键的是告诉你线程卡在哪一行、等哪把锁、这把锁被谁持有。
可视化工具
本地开发或测试环境,也可以用 GUI 工具看线程。
| 工具 | 作用 |
|---|---|
jconsole |
监控 JVM 内存、线程、类加载等信息 |
| VisualVM | 查看线程、CPU、内存、堆栈、对象分配等 |
它们适合辅助定位,但线上排障不要只依赖 GUI。最通用、最容易落地的还是 jps + jstack,因为服务器上通常能直接执行。
如何修复死锁
修复死锁不要只盯着"加大超时时间"。超时只能缓解,不能解释为什么锁顺序会错。
常见修复方式:
- 固定加锁顺序,所有线程都按同一顺序获取多把锁。
- 缩小锁粒度,减少一次持有多把锁的场景。
- 用
tryLock加超时,获取不到锁时主动释放已持有资源并重试。 - 避免在持锁期间调用外部服务、数据库、RPC 这类不可控耗时操作。
用 tryLock 的思想大概是:
java
if (lockA.tryLock(1, TimeUnit.SECONDS)) {
try {
if (lockB.tryLock(1, TimeUnit.SECONDS)) {
try {
// do something
} finally {
lockB.unlock();
}
}
} finally {
lockA.unlock();
}
}
这不是鼓励所有地方都写复杂的 tryLock,而是说明:当业务确实需要多把锁时,至少要设计失败退出路径。
面试怎么答
可以这样答:
死锁通常发生在多个线程需要同时获取多把锁时。比如线程 1 持有 A 锁等待 B 锁,线程 2 持有 B 锁等待 A 锁,就会互相等待,程序无法继续。
死锁有四个必要条件:互斥、占有且等待、不可抢占、循环等待。解决死锁就是破坏其中一个条件,项目里最常见的是固定加锁顺序,避免循环等待。
诊断时可以先用 jps -l 找 Java 进程,再用 jstack -l pid 查看线程栈。如果有 Java 级死锁,jstack 会打印 deadlock 信息,并指出线程持有和等待的锁。也可以用 jconsole、VisualVM 这类工具辅助查看线程状态。