抱歉,您的浏览器无法访问本站
本页面需要浏览器支持(启用)JavaScript
了解详情 >

前言

线程死锁是多线程编程中的一种常见问题,指多个线程因相互等待对方释放资源而陷入无限阻塞的状态。以下是关于线程死锁的详细说明及避免方法

线程死锁的定义

当两个或多个线程在执行过程中,因竞争资源而陷入相互等待的状态(每个线程都在等待其他线程释放其所需的资源),导致程序无法继续执行时,称为死锁

经典死锁场景示例

  • 线程A持有资源1,请求资源2;
  • 线程B持有资源2,请求资源1;
  • 两者互相等待,无法推进。

死锁的四个必要条件

  • 互斥条件:资源一次只能被一个线程独占使用。
  • 持有并等待:线程在持有至少一个资源的同时,等待其他资源。
  • 不可抢占:资源只能由持有者主动释放,不可被强制剥夺。
  • 循环等待:存在一个线程-资源的循环等待链(如T1等待T2的资源,T2等待T1的资源)。

只有同时满足这四个条件时,死锁才会发生。

避免死锁的策略

通过破坏死锁的四个必要条件之一即可避免死锁:

破坏循环等待

资源有序分配法:对所有资源类型进行全局排序,线程必须按固定顺序申请资源。

示例:若资源A和B的编号为1和2,所有线程必须先申请A再申请B。

破坏持有并等待

一次性申请所有资源:线程在运行前一次性申请所需全部资源,否则不执行。可通过 java.util.concurrent 中的 Lock 类实现。

破坏不可抢占

允许系统强制回收资源(如超时机制或事务回滚)。

示例:Java 中 tryLock() 方法支持超时,若获取锁失败则释放已有锁并重试。

破坏互斥条件

通过设计无锁结构(如CAS操作)或共享资源(如读写锁中的读锁)。

示例:使用 ReadWriteLock 允许多个线程同时读。

其他实用方法

超时机制:为锁的获取设置超时时间(如 tryLock(500ms)),超时后释放已有资源并重试。
死锁检测与恢复:系统定期检测死锁(如通过资源分配图),发现后终止线程或回滚操作。
减少锁粒度:使用细粒度锁(如 ConcurrentHashMap 的分段锁)减少竞争。
避免嵌套锁:尽量不在持有一个锁时申请另一个锁。

后记

避免死锁的核心是破坏四个必要条件中的至少一个,最常用的方法是资源有序分配一次性申请所有资源。在实际开发中,合理设计锁的获取顺序、减少锁的持有时间、使用高层并发工具(如 ExecutorService)能有效降低死锁风险。

评论