优秀的编程知识分享平台

网站首页 > 技术文章 正文

Java死锁

nanyue 2025-05-23 18:59:30 技术文章 7 ℃

java死锁

在Java编程中,死锁是指两个或多个线程互相等待对方释放资源的状态,每个线程都持有至少一个资源,并在等待其他线程持有的资源。由于它们都不释放自己已占有的资源,导致所有涉及的线程都无法继续执行下去,形成一种僵持状态。

死锁产生的必要条件

死锁的发生通常需要满足以下四个条件:

  1. 互斥条件:一个资源每次只能被一个线程使用。
  2. 请求与保持条件:一个线程因请求资源而阻塞时,对已经获得的资源保持不放。
  3. 不可剥夺条件:线程已获得的资源,在未使用完之前不能强行剥夺。
  4. 循环等待条件:若干线程之间形成了一种头尾相接的循环等待资源关系。

如何避免和解决死锁问题

为了避免死锁的发生,可以采取以下几种策略:

  • 破坏“请求与保持”条件:采用资源预先分配策略,即每个线程开始执行前申请所需的全部资源,仅当系统能满足进程所需的全部资源才允许其开始执行。
  • 破坏“不可剥夺”条件:允许进程强行从占有者那里夺取某些资源。即当某个进程已保持了某些资源,又申请新的资源而不能立即得到满足时,必须释放它所保持的所有资源,待以后需要时再重新申请。
  • 破坏“循环等待”条件:采用资源有序分配法,将系统中的所有资源编号,要求每个进程按照递增的顺序请求资源,这样就不会形成环路,从而避免死锁。
  • 死锁检测:定期检查系统是否有死锁的情况发生。如果检测到死锁,则采取措施恢复系统,如撤销一个或多个进程以解除死锁状态。
  • 缩小锁粒度:使用细粒度锁(如 ConcurrentHashMap 分段锁)。

实际应用中的注意事项

  • 应当注意避免嵌套锁定(即一个线程同时获取多个锁),或者确保所有线程获取锁的顺序是一致的。
  • 使用定时锁等高级特性来减少死锁的风险。例如,java.util.concurrent.locks.ReentrantLock 提供了带有超时的尝试获取锁的方法 tryLock(long timeout, TimeUnit unit),这可以避免无限期等待锁,从而降低死锁的可能性。

死锁检测与工具

  • 命令行工具

jstack:生成线程快照,检查死锁线程的堆栈信息。

示例命令:

jstack <pid> | grep "deadlock"

Arthas命令:

thread -b
  • 可视化工具

VisualVM/JConsole:监控线程状态,识别阻塞和死锁。

  • 代码检测

通过 ThreadMXBean 查找死锁线程:

import java.lang.management.ManagementFactory;
import java.lang.management.ThreadInfo;
import java.lang.management.ThreadMXBean;

public class DeadlockDetector {
    public static void main(String[] args) {
        // 创建两个资源对象
        Object resource1 = new Object();
        Object resource2 = new Object();

        // 线程1:先拿resource1,再请求resource2
        Thread thread1 = new Thread(() -> {
            synchronized (resource1) {
                System.out.println("Thread1: locked resource1");
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                }
                synchronized (resource2) {
                    System.out.println("Thread1: locked resource2");
                }
            }
        }, "Thread1");

        // 线程2:先拿resource2,再请求resource1
        Thread thread2 = new Thread(() -> {
            synchronized (resource2) {
                System.out.println("Thread2: locked resource2");
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                }
                synchronized (resource1) {
                    System.out.println("Thread2: locked resource1");
                }
            }
        }, "Thread2");

        // 启动线程
        thread1.start();
        thread2.start();

        // 主线程等待一段时间后检查死锁
        try {
            thread1.join(2000);
            thread2.join(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        detectDeadlocks();
    }

    public static void detectDeadlocks() {
        ThreadMXBean bean = ManagementFactory.getThreadMXBean();
        long[] deadlockedThreadIds = bean.findDeadlockedThreads();

        if (deadlockedThreadIds != null && deadlockedThreadIds.length > 0) {
            System.out.println("发现死锁线程:");
            for (long tid : deadlockedThreadIds) {
                ThreadInfo info = bean.getThreadInfo(tid);
                System.out.println("线程ID: " + tid + ", 名称: " + info.getThreadName());
            }
        } else {
            System.out.println("未发现死锁");
        }
    }
}

执行结果:

Thread1: locked resource1
Thread2: locked resource2
发现死锁线程:
线程ID: 30, 名称: Thread1
线程ID: 31, 名称: Thread2
最近发表
标签列表