关于死锁问题的学习总结

死锁产生的完整链条:

复制代码
互斥 → 持有并等待 → 非抢占 → 循环等待 → 死锁
  1. 互斥:资源只能独占使用(必要条件)

  2. 持有并等待:线程持有资源同时请求新资源

  3. 非抢占:已分配资源不能被强制剥夺

  4. 循环等待:线程间形成资源等待环

破坏各个条件的策略对比:

破坏的条件 可行性 Java实现方法 缺点
互斥 困难 使用无锁数据结构、原子变量 不适用所有场景
持有并等待 可行 原子性地获取所有资源 可能降低并发性
非抢占 可行 使用tryLock、超时机制 需要回退重试逻辑
循环等待 最可行 固定资源获取顺序 需要全局排序策略

实际工程建议

避免死锁的最佳实践:

  1. 尽量使用单锁:减少多锁交互的机会

  2. 固定锁顺序:全局约定锁的获取顺序

  3. 使用超时锁:用Lock接口的tryLock方法代替synchronized,这样可以设置超时时间,避免无限期等待

  4. 使用更高级的并发工具:如使用java.util.concurrent包中的并发类,它们设计时已经考虑了死锁问题

  5. 静态分析工具:使用FindBugs、SpotBugs检测潜在死锁

  6. 监控和检测:使用ThreadMXBean.findDeadlockedThreads()

实际使用 -- 典型的死锁示例:

如果两个线程分别同时执行method1和method2,可能会发生以下情况:

  1. 线程A进入method1,获得lock1。

  2. 线程B进入method2,获得lock2。

  3. 线程A在获得lock1的状态下试图获取lock2,但lock2被线程B持有,所以线程A等待。

  4. 线程B在获得lock2的状态下试图获取lock1,但lock1被线程A持有,所以线程B等待。

这样,两个线程互相等待对方释放锁,导致死锁。

进一步带入到死锁的产生的完整链条:

  1. 线程A进入method1,获得lock1(锁(这个资源)独占,达成互斥条件)。

  2. 线程B进入method2,获得lock2(锁(这个资源)独占,达成互斥条件)。

  3. 线程A在获得lock1的状态下试图获取lock2(线程持有资源同时请求新资源),但lock2被线程B持有(抢不到,非抢占),所以线程A等待(循环等待)。

  4. 线程B在获得lock2的状态下试图获取lock1(线程持有资源同时请求新资源),但lock1被线程A持有(抢不到,非抢占),所以线程B等待(循环等待)。

死锁达成。

单个锁也可能发生死锁问题(永久等待)
java 复制代码
class Resource {
    private final Object mLock = new Object();
    private boolean available = false;
    
    public void acquire() throws InterruptedException {
        synchronized (mLock) {
            while (!available) {
                mLock.wait();  // 释放锁,等待被唤醒
            }
            available = false;
        }
    }
    
    public void release() {
        synchronized (mLock) {
            available = true;
            mLock.notify(); // 唤醒
        }
    }
}
// 问题:如果release()从未被调用,那么acquire()线程将永远等待(排除虚假唤醒和线程中断的程序逻辑外的情况)

拓展 -- 读写锁实现(简单实现):

java 复制代码
import java.util.concurrent.locks.ReentrantReadWriteLock;

// 1. 读读不互斥(多个读可以同时进行)
// 2. 读写互斥(写时不能读,读时不能写)
// 3. 写写互斥(同一时间只有一个能写)
public class CorrectReadWriteLock {
    private final ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock();
    private final ReentrantReadWriteLock.ReadLock readLock = rwLock.readLock();
    private final ReentrantReadWriteLock.WriteLock writeLock = rwLock.writeLock();
    private int data;
    
    public void read() {
        readLock.lock();  // 多个读线程可以同时获取
        try {
            // 安全地读取数据
            System.out.println("Read: " + data);
        } finally {
            readLock.unlock();
        }
    }
    
    public void write(int value) {
        writeLock.lock();  // 写锁独占
        try {
            data = value;
            System.out.println("Write: " + data);
        } finally {
            writeLock.unlock();
        }
    }
}
相关推荐
万亿少女的梦1687 分钟前
基于Spring Boot、Java与MySQL的网络订餐系统设计与实现
java·spring boot·mysql·系统设计·网络订餐
咩咩啃树皮8 小时前
第40篇:Vue3组件化开发精讲——组件拆分、复用、父子通信、工程化架构
java·前端·架构
鱟鲥鳚9 小时前
Spring Boot 集成 LangChain4j:从模型调用到 Tool Calling(Demo版)
java·spring boot
大模型码小白10 小时前
【Python零基础教程】继承、多态与魔法函数:面向对象编程三大核心特性详解
java·大数据·开发语言·人工智能·python·ai编程
腾渊信息科技公司11 小时前
Spring Boot对接MES实战:视觉检测数据自动同步方案
java·人工智能·spring boot·后端·计算机视觉·ai·软件需求
爱笑的源码基地12 小时前
高并发 Redis 缓存门诊HIS系统源码,含财务统计药房进销存
java·程序·门诊系统·诊所系统·云诊所源码
wuqingshun31415912 小时前
TCP超时重传机制是为了解决什么问题?
java
HLC++12 小时前
Linux的进程间通信
android·linux·服务器
莫逸风15 小时前
【AgentScope 2.0】 0. 学习指南
java·llm·agent·agentscope
z1234567898615 小时前
2026最新两款AI编程工具深度对比实测
java·数据库·ai编程