前言
线程死锁是多线程编程中的一种常见问题,指多个线程因相互等待对方释放资源而陷入无限阻塞的状态。以下是关于线程死锁的详细说明及避免方法
线程死锁的定义
当两个或多个线程在执行过程中,因竞争资源而陷入相互等待的状态(每个线程都在等待其他线程释放其所需的资源),导致程序无法继续执行时,称为死锁。
经典死锁场景示例:
- 线程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
)能有效降低死锁风险。