Java死锁

一、什么是死锁?

死锁是并发编程中最隐蔽、最危险的问题之一,指两个或多个线程在执行过程中,因互相持有对方需要的资源而陷入无限等待的状态,若无外力干涉,所有线程将永远阻塞,程序无法继续运行。

简单来说:你拿着我要的锁,我拿着你要的锁,谁都不放手,谁都动不了。

二、死锁产生的 4 个必要条件

死锁并非随机出现,必须同时满足以下 4 个条件才会发生,破坏任意一个即可避免死锁:

  1. 互斥条件:资源同一时间只能被一个线程持有,其他线程必须等待。
  2. 请求与保持条件:线程已经持有至少一个资源,又去请求其他资源,且不释放已持有的资源。
  3. 不可剥夺条件:线程持有的资源只能由自己主动释放,不能被其他线程强行抢占。
  4. 循环等待条件:多个线程之间形成首尾相接的循环等待资源关系,如 A 等 B、B 等 C、C 等 A。

三、死锁经典场景与代码示例

最常见的死锁场景是线程交叉获取多把锁,以下是极简可复现代码:

复制代码
public class DeadLockDemo {
    private static final Object lockA = new Object();
    private static final Object lockB = new Object();

    public static void main(String[] args) {
        // 线程1:先拿lockA,再拿lockB
        new Thread(() -> {
            synchronized (lockA) {
                System.out.println("线程1获取lockA");
                try { Thread.sleep(100); } catch (InterruptedException e) {}
                synchronized (lockB) {
                    System.out.println("线程1获取lockB");
                }
            }
        }).start();

        // 线程2:先拿lockB,再拿lockA
        new Thread(() -> {
            synchronized (lockB) {
                System.out.println("线程2获取lockB");
                try { Thread.sleep(100); } catch (InterruptedException e) {}
                synchronized (lockA) {
                    System.out.println("线程2获取lockA");
                }
            }
        }).start();
    }
}

运行后,两个线程会分别持有一把锁,互相等待对方释放锁,触发死锁。

四、如何检测死锁?

实际项目中死锁不会直接报错,需要通过工具定位:

  1. jstack 命令 执行jps找到进程 ID,再用jstack -l 进程ID查看线程堆栈,日志中会明确提示Found one Java-level deadlock,并列出死锁线程与锁信息。
  2. JConsole/JVisualVM 打开可视化工具,连接进程后查看线程 面板,点击检测死锁,可直观看到死锁的线程与资源依赖关系。
  3. Arthas 阿里开源诊断工具,使用thread -b命令可直接定位阻塞的死锁线程。

五、死锁的避免与解决方法

核心思路:破坏死锁 4 个必要条件中的任意一个,推荐以下实用方案:

  1. 固定锁获取顺序 所有线程按相同顺序获取锁,避免循环等待,这是最常用、最安全的方案。
  2. 避免同时持有多把锁重构代码,让每个线程只持有一把锁,减少锁嵌套,从源头降低死锁概率。
  3. 使用定时锁Lock.tryLock(long time, TimeUnit unit)替代synchronized,获取锁超时则放弃并释放已持有的锁。
  4. 响应中断 使用lockInterruptibly()支持锁中断,等待锁时可被外部中断,避免无限等待。
  5. 减少锁粒度拆分大锁为多个小锁,降低线程竞争概率,提升并发效率。

六、总结

死锁是并发编程的经典陷阱,看似简单却极易在复杂业务中隐藏。理解 4 个必要条件、掌握检测工具、遵守编码规范,就能有效避免死锁。

开发中牢记:少用锁嵌套、固定加锁顺序、优先使用定时锁,让你的并发程序稳定运行,远离死锁困扰。

相关推荐
讳疾忌医丶5 分钟前
深度拆解 RocksDB 内核:基于 C++17 的 FIFO 调度状态机与温度阶梯自愈设计
java·c++·算法·架构
lsswear20 分钟前
Python 并发 线程
开发语言·python
郑州光合科技余经理29 分钟前
国际版外卖系统:税率字段怎么和订单主流程解耦
android·java·开发语言·前端·后端·php·ai编程
毅炼29 分钟前
Rover-Suite 开源发布:轻量服务注册中心与网关一体化方案
java·后端·系统架构·gateway
软件课代表CiCi44 分钟前
运行库是什么?电脑缺运行库怎么办?c++运行库缺失修复全攻略
开发语言·c++·windows·电脑·dll修复·dll丢失·运行库
梦想平凡1 小时前
百游棋牌源代码开发搭建教程(十):隔离部署、备份恢复与双端验收
java·前端·javascript·数据库·源代码管理
Ivanqhz1 小时前
MLIR OpBuilder
开发语言·python·mlir
sunshine22 girl2 小时前
Idea中如何搜索
java·ide·intellij-idea
猫吻鱼2 小时前
【AI 01】【Spring AI 基础使用】
java·spring·spring ai
红宝村村长2 小时前
windows笔记本双系统ubuntu设置
开发语言