Java死锁避免的五种方法举例总结

 更新时间:2025年05月07日 08:33:14   作者:EthanMilk  
这篇文章主要介绍了Java死锁避免的五种方法,包括按固定顺序加锁、使用tryLock+超时、一次性申请所有资源、使用Lock替代synchronized以及检测与恢复,需要的朋友可以参考下

前言

死锁(Deadlock)是指多个线程互相持有对方需要的资源,导致所有线程都无法继续执行的情况。Java 中可以通过以下方法避免死锁:

造成死锁的⼏个原因:

  • ⼀个资源每次只能被⼀个线程使⽤

  • ⼀个线程在阻塞等待某个资源时,不释放已占有资源

  • ⼀个线程已经获得的资源,在未使⽤完之前,不能被强⾏剥夺

  • 若⼲线程形成头尾相接的循环等待资源关系

这是造成死锁必须要达到的4个条件,如果要避免死锁,只需要不满⾜其中某⼀个条件即可。⽽其中前3 个条件是作为锁要符合的条件,所以要避免死锁就需要打破第4个条件,不出现循环等待锁的关系。

在开发过程中:

  • 要注意加锁顺序,保证每个线程按同样的顺序进⾏加锁

  • 要注意加锁时限,可以针对所设置⼀个超时时间

  • 要注意死锁检查,这是⼀种预防机制,确保在第⼀时间发现死锁并进⾏解决

1. 死锁产生的四个必要条件

要避免死锁,首先要理解死锁发生的条件(必须全部满足):

  • 互斥条件:资源一次只能被一个线程占用。

  • 占有并等待:线程持有资源的同时,等待其他资源。

  • 不可抢占:线程持有的资源不能被其他线程强行夺走。

  • 循环等待:多个线程形成环形等待链(A 等 B,B 等 C,C 等 A)。

只要破坏其中任意一个条件,就能避免死锁!

2. 避免死锁的 5 种方法

方法 1:按固定顺序获取锁(破坏循环等待)

核心思想:所有线程按相同的顺序申请锁,避免循环依赖。

✅ 正确示例

public void transfer(Account from, Account to, int amount) {
    // 规定:先锁 id 小的账户,再锁 id 大的账户
    Account first = from.getId() < to.getId() ? from : to;
    Account second = from.getId() < to.getId() ? to : from;

    synchronized (first) {
        synchronized (second) {
            if (from.getBalance() >= amount) {
                from.debit(amount);
                to.credit(amount);
            }
        }
    }
}

为什么能避免死锁?所有线程都按 id 顺序加锁,不会出现 A 锁 1 → 等 2 和 B 锁 2 → 等 1 的情况。

方法 2:使用 tryLock + 超时(破坏占有并等待)

核心思想:如果拿不到锁,就释放已持有的锁,避免无限等待。

✅ 正确示例(ReentrantLock)

public boolean transfer(Account from, Account to, int amount, long timeout) throws InterruptedException {
    long startTime = System.currentTimeMillis();
    while (true) {
        if (from.lock.tryLock()) { // 尝试获取第一把锁
            try {
                if (to.lock.tryLock()) { // 尝试获取第二把锁
                    try {
                        if (from.getBalance() >= amount) {
                            from.debit(amount);
                            to.credit(amount);
                            return true;
                        }
                    } finally {
                        to.lock.unlock();
                    }
                }
            } finally {
                from.lock.unlock(); // 释放第一把锁
            }
        }
        // 超时检查
        if (System.currentTimeMillis() - startTime >= timeout) {
            return false;
        }
        Thread.sleep(100); // 避免 CPU 忙等待
    }
}

优点

  • 不会无限等待,超时后可以重试或回滚。

  • 适用于高并发场景(如支付系统)。

方法 3:一次性申请所有资源(破坏占有并等待)

核心思想:使用一个全局锁,一次性申请所有需要的资源。

✅ 正确示例

public class AccountManager {
    private static final Object globalLock = new Object();

    public void transfer(Account from, Account to, int amount) {
        synchronized (globalLock) { // 全局锁保护所有账户
            if (from.getBalance() >= amount) {
                from.debit(amount);
                to.credit(amount);
            }
        }
    }
}

缺点:并发度降低(所有转账操作串行化)。

方法 4:使用 Lock 替代 synchronized(更灵活的控制)

ReentrantLock 比 synchronized 更灵活,可以避免死锁:

Lock lock1 = new ReentrantLock();
Lock lock2 = new ReentrantLock();

public void methodA() {
    boolean gotLock1 = false;
    boolean gotLock2 = false;
    try {
        gotLock1 = lock1.tryLock(1, TimeUnit.SECONDS);
        gotLock2 = lock2.tryLock(1, TimeUnit.SECONDS);
        if (gotLock1 && gotLock2) {
            // 执行业务逻辑
        }
    } catch (InterruptedException e) {
        Thread.currentThread().interrupt();
    } finally {
        if (gotLock1) lock1.unlock();
        if (gotLock2) lock2.unlock();
    }
}

方法 5:检测与恢复(允许死锁发生,但能自动恢复)

适用于复杂系统(如数据库、分布式系统):

1.检测死锁

使用 ThreadMXBean 查找死锁:

ThreadMXBean bean = ManagementFactory.getThreadMXBean();
long[] threadIds = bean.findDeadlockedThreads();
if (threadIds != null) {
    System.out.println("检测到死锁!");
}

2.恢复策略

强制终止某个线程(如 thread.interrupt())。

回滚事务(数据库场景)。

3. 死锁案例分析

❌ 错误代码(会导致死锁)

public void transfer(Account from, Account to, int amount) {
    synchronized (from) {  // 线程1:锁 from → 等 to
        synchronized (to) { // 线程2:锁 to → 等 from
            if (from.getBalance() >= amount) {
                from.debit(amount);
                to.credit(amount);
            }
        }
    }
}

死锁场景

  • 线程1:transfer(accountA, accountB, 100)

  • 线程2:transfer(accountB, accountA, 200)

  • 结果:互相等待,死锁!

4. 总结

方法适用场景优点缺点
固定顺序加锁简单锁依赖实现简单需要全局排序规则
tryLock + 超时高并发系统避免无限等待代码复杂度高
全局锁低并发场景绝对安全性能差(串行化)
Lock 替代 synchronized需要更细粒度控制灵活(可中断、超时)需手动释放锁
检测与恢复数据库、分布式系统适用于复杂场景实现复杂

最佳实践

  • 尽量用 tryLock + 超时(推荐 ReentrantLock)。

  • 如果必须用 synchronized,按固定顺序加锁

  • 避免嵌套锁(如 synchronized 里再调 synchronized 方法)。

  • 使用工具检测死锁(如 jstackThreadMXBean)。

到此这篇关于Java死锁避免的五种方法的文章就介绍到这了,更多相关Java死锁避免内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Springboot整合freemarker 404问题解决方案

    Springboot整合freemarker 404问题解决方案

    这篇文章主要介绍了Springboot整合freemarker 404问题解决方案,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-05-05
  • Springboot整合Activiti操作详解

    Springboot整合Activiti操作详解

    这篇文章主要给大家详细介绍了Springboot整合Activiti的操作流程,文中流程步骤和代码示例介绍的非常详细,具有一定的参考价值,需要的朋友可以参考下
    2023-07-07
  • SpringSecurity权限控制实现原理解析

    SpringSecurity权限控制实现原理解析

    这篇文章主要介绍了SpringSecurity权限控制实现原理解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-03-03
  • SpringCloud-Hystrix实现原理总结

    SpringCloud-Hystrix实现原理总结

    通过hystrix可以解决雪崩效应问题,它提供了资源隔离、降级机制、融断、缓存等功能。接下来通过本文给大家分享SpringCloud-Hystrix实现原理,感兴趣的朋友一起看看吧
    2021-05-05
  • java类和对象原理与用法分析

    java类和对象原理与用法分析

    这篇文章主要介绍了java类和对象原理与用法,结合实例形式分析了java类和对象的相关概念、功能、原理、使用技巧与操作注意事项,需要的朋友可以参考下
    2020-02-02
  • Java中的字符型文件流FileReader和FileWriter详细解读

    Java中的字符型文件流FileReader和FileWriter详细解读

    这篇文章主要介绍了Java中的字符型文件流FileReader和FileWriter详细解读,与字节型文件流不同,字节型文件流读取和写入的都是一个又一个的字节,而字符型文件流操作的单位是一个又一个的字符,字符型流认为一个字母是一个字符,而一个汉字也是一个字符,需要的朋友可以参考下
    2023-10-10
  • Java操作MongoDB事务未生效的常见场景及解决方案

    Java操作MongoDB事务未生效的常见场景及解决方案

    在 Java 开发中,使用 MongoDB 存储数据时,事务的正确使用至关重要,然而,在实际开发过程中,经常会遇到 MongoDB 事务没有生效的情况,本文我将结合多年实践经验,深入剖析事务未生效的常见场景,并给出详细的解决方案,需要的朋友可以参考下
    2025-07-07
  • SpringBoot快速实现IP地址解析的全攻略

    SpringBoot快速实现IP地址解析的全攻略

    在当今的互联网应用中,IP地址解析已成为许多系统不可或缺的功能,这篇文章主要为大家详细介绍了如何使用SpringBoot快速实现IP地址解析,有需要的小伙伴可以了解下
    2026-02-02
  • IntelliJ IDEA将导入的项目转成maven项目

    IntelliJ IDEA将导入的项目转成maven项目

    这篇文章主要介绍了IntelliJ IDEA将导入的项目转成maven项目,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-09-09
  • MyBatis学习笔记(二)之关联关系

    MyBatis学习笔记(二)之关联关系

    这篇文章主要介绍了MyBatis学习笔记(二)之关联关系 的相关资料,需要的朋友可以参考下
    2016-02-02

最新评论