登录
首页 >  文章 >  java教程

Java死锁原因及解决方法解析

时间:2026-02-25 20:15:50 357浏览 收藏

Java死锁是多线程环境下因锁资源争用而陷入的“僵持困局”——当多个线程各自持有对方所需的锁、又拒绝释放时,所有线程便永久阻塞、系统响应骤降甚至停滞;它并非简单编码错误,而是由互斥、占有并等待、不可剥夺和循环等待四大条件共同触发的深层并发陷阱,典型如两线程以相反顺序嵌套获取lockA和lockB;更隐蔽的是那些仅在高并发特定时序下才浮现的“幽灵死锁”,它们不抛异常、不报错,却悄然拖垮性能——掌握jstack检测、统一加锁顺序、超时重试等实战策略,才能真正驯服这一并发世界中最棘手的隐形杀手。

在Java中什么是死锁_Java死锁产生原因解析

死锁不是代码写错了,而是多个线程互相持有对方需要的锁,谁也不肯先放——结果全部卡住不动。

死锁发生的四个必要条件

Java 中的死锁必须同时满足以下四点,缺一不可:

  • 互斥:锁是独占的,synchronizedReentrantLock 都默认满足
  • 占有并等待:一个线程已持有一个锁,又去申请另一个锁(比如先 synchronized(A.class),再 synchronized(B.class)
  • 不可剥夺:Java 中锁不能被强制释放,持有者必须主动退出同步块或调用 unlock()
  • 循环等待:线程 T1 等 T2 的锁,T2 又等 T1 的锁,形成闭环

最典型的死锁代码长什么样

下面这段代码在多线程环境下极大概率触发死锁:

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) {
                try { Thread.sleep(100); } catch (InterruptedException e) {}
                synchronized (lockB) {
                    System.out.println("t1 done");
                }
            }
        });

        Thread t2 = new Thread(() -> {
            synchronized (lockB) {
                try { Thread.sleep(100); } catch (InterruptedException e) {}
                synchronized (lockA) {
                    System.out.println("t2 done");
                }
            }
        });

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

关键问题在于:两个线程获取 lockAlockB 的顺序不一致。t1 先 A 后 B,t2 先 B 后 A —— 这就是循环等待的温床。

如何检测和避免死锁

JDK 自带工具能帮你发现运行中的死锁:

  • jstack 查看线程栈,输出里如果出现 Found 1 deadlock. 就确认了
  • 在 JConsole 或 VisualVM 的「线程」页签中点「Detect Deadlock」按钮
  • 避免嵌套锁:尽量只在一个同步块内操作,不跨块持锁再申请
  • 统一加锁顺序:所有线程按相同顺序获取锁(比如按对象哈希值排序:if (a.hashCode() )
  • 使用带超时的锁:lock.tryLock(1, TimeUnit.SECONDS) 失败就释放已有锁,重试或放弃

真正难处理的不是写出来就死锁的代码,而是那些在高并发、特定时序下才暴露的锁竞争路径——它们往往藏在日志里不报错,只让系统慢慢变慢、响应延迟飙升。

好了,本文到此结束,带大家了解了《Java死锁原因及解决方法解析》,希望本文对你有所帮助!关注golang学习网公众号,给大家分享更多文章知识!

资料下载
相关阅读
更多>
最新阅读
更多>
课程推荐
更多>