多线程基础系列-线程死锁

文章目录

  • 多线程基础系列-线程死锁
    • [1. 什么是线程死锁?](#1. 什么是线程死锁?)
    • [2. 如何检测线程死锁?](#2. 如何检测线程死锁?)
    • [3. 如何预防和避免线程死锁?](#3. 如何预防和避免线程死锁?)

多线程基础系列-线程死锁

1. 什么是线程死锁?

  • 定义:两个或多个线程互相持有对方需要的资源并永久等待,导致线程都无法继续执行。
  • 必要条件(同时满足才会发生):
    • 互斥:资源一次只能被一个线程占用。
    • 请求并保持:持有资源的同时再请求新资源。
    • 不可剥夺:已获得的资源不能被强制抢占。
    • 循环等待:形成资源等待环路。
  • 一个经典示例:线程 A 持有锁 L1 等待 L2;线程 B 持有锁 L2 等待 L1,循环等待 → 死锁。

    示例代码(故意制造死锁):
java 复制代码
public class DeadlockDemo {
    private static final Object L1 = new Object();
    private static final Object L2 = new Object();

    public static void main(String[] args) {
        Thread t1 = new Thread(() -> {
            synchronized (L1) {
                System.out.println("T1 get L1");
                sleep(100);
                System.out.println("T1 waiting get L2");
                synchronized (L2) {
                    System.out.println("T1 acquired L1 and L2");
                }
            }
        }, "T1");

        Thread t2 = new Thread(() -> {
            synchronized (L2) {
                System.out.println("T2 get L2");
                sleep(100);
                System.out.println("T2 waiting get L1");
                synchronized (L1) {
                    System.out.println("T2 acquired L2 and L1");
                }
            }
        }, "T2");

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

    private static void sleep(long ms) {
        try {
            Thread.sleep(ms);
        } catch (InterruptedException ignored) {}
    }
}

2. 如何检测线程死锁?

  • 线程 Dump(推荐首选)
    • jstack <pid>:直接输出线程栈,底部会有 "Found one Java-level deadlock"。
    • jcmd <pid> Thread.print:与 jstack 类似,可多次采样比对。
    • jmap -histo:结合对象分布,辅助判断锁竞争热点。
  • 可视化工具
    • JConsole / VisualVM:线程页签可直接标红死锁线程。

这里以 JConsole 工具为例进行演示

  • 第一步运行我们编写的死锁示例
  • 第二步我们要找到 JDK 的 bin 目录,找到 jconsole 并双击打开,并选择我死锁demo的进程
  • 第三步:[线程]一栏点击"检测死锁"
  • 最后就可以看到死锁线程的相关说明

3. 如何预防和避免线程死锁?

  • 统一加锁顺序
    • 约定所有线程获取多把锁的顺序,避免循环等待(如总是先锁 L1 再锁 L2)。
  • 减少锁粒度与持有时间
    • 缩小同步块范围;避免在持锁期间执行 IO/远程调用。
  • 尽量避免嵌套锁
    • 能拆开的锁拆开;必要时将逻辑重构为无嵌套的步骤。
  • 使用可中断/可超时锁
    • ReentrantLock.tryLock(timeout, unit):拿不到锁及时放弃,避免永久等待。
java 复制代码
ReentrantLock lockA = new ReentrantLock();
if (lockA.tryLock(200, TimeUnit.MILLISECONDS)) {
    try {
        // 临界区
    } finally {
        lockA.unlock();
    }
} else {
    // 降级或重试逻辑
}
  • 使用更高层并发工具
    • java.util.concurrent 中的 ConcurrentHashMapBlockingQueueSemaphoreStampedLock 等,减少显式锁。
  • 不可变与无共享设计
    • 尽量使用不可变对象或线程封闭(Thread confinement),减少共享状态。
  • 分离职责与锁分段
    • 按领域拆分锁(lock striping),避免大锁串行化。
  • 定期监控与压测
    • 在预生产或压测环境跑热点场景,定期采集线程 Dump 进行巡检。
相关推荐
不会Android的潘潘3 分钟前
受限系统环境下的 WebView 能力演进:车载平台 Web 渲染异常的根因分析与优化实践
android·java·前端·aosp
建军啊6 分钟前
java web常见lou洞
android·java·前端
阳无6 分钟前
宝塔部署的前后端项目从IP访问改成自定义域名访问
java·前端·部署
Pluchon10 分钟前
硅基计划4.0 算法 动态规划进阶
java·数据结构·算法·动态规划
会游泳的石头11 分钟前
Java 异步事务完成后的监听器:原理、实现与应用场景
java·开发语言·数据库
数智工坊11 分钟前
【操作系统-IO调度】
java·服务器·数据库
黎雁·泠崖14 分钟前
Java字符串进阶:StringBuilder+StringJoiner
java·开发语言
糖猫猫cc26 分钟前
Kite:Kotlin/Java 通用的全自动 ORM 框架
java·kotlin·springboot·orm
u01040583626 分钟前
Java微服务架构:设计模式与实践
java·微服务·架构
AI_567835 分钟前
测试用例“标准化”:TestRail实战技巧,从“用例编写”到“测试报告生成”
java·python·测试用例·testrail