Java死锁问题解决方案及示例详解

 更新时间:2025年06月10日 11:35:02   作者:拾荒的小海螺  
死锁是指两个或多个线程因争夺资源而相互等待,导致所有线程都无法继续执行的一种状态,本文给大家详细介绍了Java死锁问题解决方案详解及实践样例,需要的朋友可以参考下

1、简述

死锁(Deadlock) 是指两个或多个线程因争夺资源而相互等待,导致所有线程都无法继续执行的一种状态。

死锁的四个必要条件:

  • 互斥条件:某资源一次只能被一个线程占用。
  • 占有且等待:一个线程已持有资源并等待其他线程的资源。
  • 不可剥夺:资源不能被强行从线程中剥夺。
  • 循环等待:多个线程形成一种头尾相接的等待资源关系链。

只要这四个条件同时满足,就可能产生死锁。

2、死锁示例代码

下面是一个典型的死锁示例:

public class DeadlockExample {
    private static final Object LockA = new Object();
    private static final Object LockB = new Object();

    public static void main(String[] args) {
        Thread t1 = new Thread(() -> {
            synchronized (LockA) {
                System.out.println("Thread-1: locked A");

                try { Thread.sleep(100); } catch (InterruptedException e) {}

                synchronized (LockB) {
                    System.out.println("Thread-1: locked B");
                }
            }
        });

        Thread t2 = new Thread(() -> {
            synchronized (LockB) {
                System.out.println("Thread-2: locked B");

                try { Thread.sleep(100); } catch (InterruptedException e) {}

                synchronized (LockA) {
                    System.out.println("Thread-2: locked A");
                }
            }
        });

        t1.start();
        t2.start();
    }
}

运行这段程序可能会导致 Thread-1 等待 LockBThread-2 等待 LockA,从而产生死锁。

3、如何检测死锁?

3.1 使用 jstack

当应用卡住时,使用如下命令查看线程堆栈:

jps      # 查找 Java 进程 ID
jstack <pid>

你会看到类似:

Found one Java-level deadlock:
"Thread-1": waiting to lock monitor 0x000..., which is held by "Thread-2"
...

3.2 使用 VisualVM 或 JConsole

这类工具可以图形化展示线程状态,识别死锁非常直观。

4、如何预防和解决死锁?

4.1 统一资源获取顺序(推荐)

确保多个线程获取多个锁时 按相同顺序加锁

public void safeMethod() {
    synchronized (LockA) {
        synchronized (LockB) {
            // 安全操作
        }
    }
}

4.2 使用 tryLock() 避免无限等待

使用 ReentrantLock 的 tryLock() 方法设置获取锁的超时时间。

import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.TimeUnit;

public class TryLockExample {
    private final ReentrantLock lockA = new ReentrantLock();
    private final ReentrantLock lockB = new ReentrantLock();

    public void run() {
        Thread t1 = new Thread(() -> {
            try {
                if (lockA.tryLock(1, TimeUnit.SECONDS)) {
                    System.out.println("Thread-1: locked A");
                    Thread.sleep(100);

                    if (lockB.tryLock(1, TimeUnit.SECONDS)) {
                        System.out.println("Thread-1: locked B");
                        lockB.unlock();
                    }
                    lockA.unlock();
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });

        Thread t2 = new Thread(() -> {
            try {
                if (lockB.tryLock(1, TimeUnit.SECONDS)) {
                    System.out.println("Thread-2: locked B");
                    Thread.sleep(100);

                    if (lockA.tryLock(1, TimeUnit.SECONDS)) {
                        System.out.println("Thread-2: locked A");
                        lockA.unlock();
                    }
                    lockB.unlock();
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });

        t1.start();
        t2.start();
    }

    public static void main(String[] args) {
        new TryLockExample().run();
    }
}

4.3 使用 java.util.concurrent 包

避免使用低级的 synchronized,使用高级并发工具如 ExecutorServiceSemaphoreLock 等更可控的工具。

4.4 死锁检测与恢复

一些大型系统中,可以通过定期扫描线程状态,自动检测死锁并重启部分线程或服务。例如通过自定义 ThreadMXBean 检测死锁。

最佳实践小结:

技术手段说明
加锁顺序统一所有线程按相同顺序获取资源
tryLock + timeout尝试加锁失败后避免长时间阻塞
lock 分解将大锁拆分成小锁减少竞争
并发工具替代 synchronized使用 ReentrantLock、Semaphore 等
死锁检测工具使用 jstack、VisualVM 等工具

5、结语

死锁是多线程编程中不可忽视的问题,但并不是无法避免。只要我们在设计时保持锁的有序性,并结合现代并发工具进行控制,绝大多数死锁问题都是可以预防的。

以上就是Java死锁问题解决方案及示例详解的详细内容,更多关于Java死锁问题解决的资料请关注脚本之家其它相关文章!

相关文章

  • idea mybatis配置log4j打印sql语句的示例

    idea mybatis配置log4j打印sql语句的示例

    本篇文章主要介绍了idea mybatis配置log4j打印sql语句的示例,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-01-01
  • Springcloud feign传日期类型参数报错的解决方案

    Springcloud feign传日期类型参数报错的解决方案

    这篇文章主要介绍了Springcloud feign传日期类型参数报错的解决方案,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-03-03
  • springboot + mybatis-plus实现多表联合查询功能(注解方式)

    springboot + mybatis-plus实现多表联合查询功能(注解方式)

    这篇文章主要介绍了springboot + mybatis-plus实现多表联合查询功能,是最简单的一种注解方式,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-09-09
  • 基于java构造方法Vector查找元素源码分析

    基于java构造方法Vector查找元素源码分析

    本篇文章是关于ava构造方法Vector源码分析系列文章,本文主要介绍了Vector查找元素的源码分析,有需要的朋友可以借鉴参考下,希望可以有所帮助
    2021-09-09
  • IDEA Debug模式下改变各类型变量值的方法

    IDEA Debug模式下改变各类型变量值的方法

    这篇文章主要介绍了IDEA Debug模式下改变各类型变量值的方法,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-04-04
  • 详细全面解析Java泛型

    详细全面解析Java泛型

    这篇文章主要介绍了详细全面解析Java泛型,java泛型主要提高了Java 程序的类型安全,通过知道使用泛型定义的变量的类型限制,编译器可以验证类型假设,消除源代码中的许多强制类型转换等多个有点,下面我们进入文章了解更多的详细内容吧
    2022-02-02
  • spring中ioc是什么

    spring中ioc是什么

    IoC是一种让服务消费者不直接依赖于服务提供者的组件设计方式,是一种减少类与类之间依赖的设计原则。下面通过本文给大家分享spring中ioc的概念,感兴趣的朋友一起看看吧
    2017-09-09
  • Spring监听器及定时任务实现方法详解

    Spring监听器及定时任务实现方法详解

    这篇文章主要介绍了Spring监听器及定时任务实现方法详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-07-07
  • Java实现简单图书借阅系统

    Java实现简单图书借阅系统

    这篇文章主要为大家详细介绍了Java实现简单图书借阅系统,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-03-03
  • java的泛型你真的了解吗

    java的泛型你真的了解吗

    这篇文章主要为大家详细介绍了java的泛型,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下,希望能够给你带来帮助
    2022-03-03

最新评论