当前位置: 首页 > news >正文

多线程死锁

思路

原因:

多线程 + 共享资源

互斥:一个资源,只能被一个线程持有

持有并等待:

非抢占

循环等待

方案:

多线程顺序获取资源

+

tryLock 设置获取锁超时时间

使用并发集合或类:concurrenthashmap AutonomicLong

减小持有锁多粒度

ReentrantLock
支持可中断锁、公平锁、尝试锁等

多线程死锁(Deadlock)详解

死锁是多线程编程中最经典的问题之一,指 两个或多个线程互相持有对方需要的资源,导致所有线程无限等待。以下是死锁的核心概念、产生条件、排查方法和解决方案。


1. 死锁的四个必要条件

死锁发生必须同时满足以下四个条件(缺一不可):

  1. 互斥条件(Mutual Exclusion)
    资源一次只能被一个线程占用(如锁、文件句柄等)。

  2. 占有并等待(Hold and Wait)
    线程持有至少一个资源,同时等待获取其他被占用的资源。

  3. 非抢占条件(No Preemption)
    已分配给线程的资源不能被其他线程强制夺取,必须由线程主动释放。

  4. 循环等待(Circular Wait)
    存在一个线程的循环等待链,每个线程都在等待下一个线程占用的资源。


2. 死锁代码示例(Java)

java

public class DeadlockExample {private static final Object lock1 = new Object();private static final Object lock2 = new Object();public static void main(String[] args) {Thread thread1 = new Thread(() -> {synchronized (lock1) {System.out.println("Thread1 holds lock1");try { Thread.sleep(100); } catch (InterruptedException e) {}synchronized (lock2) {System.out.println("Thread1 holds lock2");}}});Thread thread2 = new Thread(() -> {synchronized (lock2) {System.out.println("Thread2 holds lock2");synchronized (lock1) {System.out.println("Thread2 holds lock1");}}});thread1.start();thread2.start();}
}

输出结果

text

Thread1 holds lock1
Thread2 holds lock2
(程序卡死,无法继续)
  • 原因

    • Thread1 持有 lock1,等待 lock2

    • Thread2 持有 lock2,等待 lock1

    • 形成 循环等待,导致死锁。


3. 如何检测死锁?

(1) 命令行工具(Linux/Java)
  • jstack(Java 线程分析工具):

    bash

    jstack <PID>  # 查看线程堆栈,搜索 "deadlock"
  • jconsole 或 VisualVM
    图形化工具中可直接检测到死锁线程。

(2) 代码中主动检测

java

ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean();
long[] threadIds = threadMXBean.findDeadlockedThreads();
if (threadIds != null) {System.out.println("Detected deadlock!");
}

4. 解决死锁的常见方法

(1) 破坏死锁的必要条件
  • 避免互斥
    使用无锁数据结构(如 ConcurrentHashMap)。

  • 避免占有并等待
    一次性申请所有资源(如通过全局锁)。

  • 允许抢占
    使用 tryLock() 超时机制(见下文)。

  • 避免循环等待
    按固定顺序获取锁(如总是先 lock1 后 lock2)。

(2) 使用超时机制

java

if (lock1.tryLock(100, TimeUnit.MILLISECONDS)) {try {if (lock2.tryLock(100, TimeUnit.MILLISECONDS)) {try {// 执行业务逻辑} finally {lock2.unlock();}}} finally {lock1.unlock();}
}
(3) 锁顺序全局化

java

// 总是先获取 hash值小的锁
int hash1 = System.identityHashCode(lock1);
int hash2 = System.identityHashCode(lock2);if (hash1 < hash2) {synchronized (lock1) {synchronized (lock2) { /* ... */ }}
} else {synchronized (lock2) {synchronized (lock1) { /* ... */ }}
}
(4) 使用高级并发工具
  • ReentrantLock
    支持可中断锁、公平锁、尝试锁等。

  • Semaphore
    控制资源访问数量。

  • CyclicBarrier / CountDownLatch
    协调多线程执行顺序。


5. 预防死锁的最佳实践

  1. 尽量减少同步代码块的范围

  2. 避免嵌套锁(如一个同步方法调用另一个同步方法)。

  3. 使用线程池,避免无限制创建线程。

  4. 设计时考虑锁顺序,避免循环等待。

  5. 监控工具预警(如 APM 工具检测线程阻塞)。


总结

关键点说明
死锁条件互斥、占有等待、非抢占、循环等待(四者缺一不可)。
检测工具jstackjconsole、VisualVM。
解决方案破坏必要条件、超时机制、锁顺序全局化、使用高级并发工具。
预防策略减少锁粒度、避免嵌套锁、设计时规划锁顺序。

死锁是并发编程中的难点,但通过合理设计和工具辅助,可以有效避免和解决!

http://www.dtcms.com/a/277913.html

相关文章:

  • 飞算Java AI开发助手:引领智能编程新风尚
  • Llama系列:Llama1, Llama2,Llama3内容概述
  • 【读书笔记】《C++ Software Design》第九章:The Decorator Design Pattern
  • HTML 基本骨架
  • [GWCTF 2019]我有一个数据库
  • SOMEIP协议与测试
  • LeetCode 2401.最长优雅子数组
  • C++数组指针与函数指针
  • 为什么要有延时回调?
  • 2024-2025-2 山东大学《软件工程与实践》期末(回忆版)
  • p4 大小写检查
  • C++高级编程,类模版成员函数类外实现
  • windows10如何安装vue开发环境
  • JAVA-springboot 整合Activemq
  • ECU(电子控制单元)是什么?
  • C++中顶层const与底层const
  • JSX 语法
  • 【前端知识】移动端APP原生应用与H5交互底层逻辑
  • Dubbo跨越分布式事务的最终一致性陷阱
  • 有效感受野(ERF)可视化工具
  • hash表的模拟--开放定址法
  • 如何将本地代码同步到远程Github仓库
  • 【Docker基础】Dockerfile指令速览:环境与元数据指令详解
  • OSPF与BGP的联动特性
  • Utils系列之内存池(MultiSizePool)
  • 【MLLM】多模态理解GLM-4.1V-Thinking模型
  • OpenVela 日志系统:从配置到落地的实操手册
  • Python装饰器(自定义装饰器和3个内置装饰器)
  • Java反射机制深度解析
  • 树莓派5-ollama-linux-arm64.tgz 下载