java

关注公众号 jb51net

关闭
首页 > 软件编程 > java > Java死锁问题解决

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

作者:拾荒的小海螺

死锁是指两个或多个线程因争夺资源而相互等待,导致所有线程都无法继续执行的一种状态,本文给大家详细介绍了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死锁问题解决的资料请关注脚本之家其它相关文章!

您可能感兴趣的文章:
阅读全文