死锁产生条件与诊断:jps、jstack、VisualVM

死锁题很容易被答成一句话:两个线程互相等待。

这句话当然对,但面试里不够。更完整的回答应该包括三层:

  1. 死锁怎么写出来的。
  2. 死锁成立需要哪些条件。
  3. 线上或本地怎么定位。

一个最典型的死锁

两个线程分别持有一把锁,又去等待对方手里的锁。

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,因为服务器上通常能直接执行。

如何修复死锁

修复死锁不要只盯着"加大超时时间"。超时只能缓解,不能解释为什么锁顺序会错。

常见修复方式:

  1. 固定加锁顺序,所有线程都按同一顺序获取多把锁。
  2. 缩小锁粒度,减少一次持有多把锁的场景。
  3. tryLock 加超时,获取不到锁时主动释放已持有资源并重试。
  4. 避免在持锁期间调用外部服务、数据库、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 这类工具辅助查看线程状态。

相关推荐
再玩一会儿看代码1 小时前
Java抽象类和接口区别_场景理解
java·开发语言·经验分享·笔记·python
用户925807911481 小时前
画图理解mysql日志机制
java·后端
javahongxi1 小时前
Spring Cloud Trace 链路实现
java·spring boot·spring cloud
海梨花1 小时前
腾讯面试高频算法题
java·算法·面试
于先生吖1 小时前
Java消息队列优化抢单逻辑,同城搬家拉货多场景业务数据库架构设计
java·开发语言·数据库架构
半个烧饼不加肉1 小时前
JS 底层探究--执行上下文
开发语言·前端·javascript
小谢小哥1 小时前
68-持续集成详解
java·后端·架构
用户925807911481 小时前
redission原理
java·后端