本文摘抄自《Java 多线程编程实战指南》核心篇 第七章小结
本章介绍了常见的线程活性故障以及相应的规避措施。
死锁会导致相关线程一直被暂停使得其任务无法进展。产生死锁的必要条件包括:资源互斥/资源不可抢夺/占用并等待资源以及循环等待资源。我们可以通过查看线程转储手工检测死锁,也可以利用 ThreadMXBean.findDeadlockedThreads() 方法进行死锁的自动检测。死锁的规避方法包括:粗锁法(使用一个粗粒度的锁代替多个锁)/锁排序法(相关线程使用全局统一的顺序申请锁)/使用 ReentrantLock.tryLock(long,TimeUnit)来申请锁/使用开放调用(在调用外部方法时不加锁)以及使用锁的替代品。使用内部锁或者使用 lock.lock() 申请的显式锁导致的死锁是无法恢复的;使用 lock.lockInterruptibly()申请的显式锁导致的死锁理论上是可恢复的,但实际可操作性不强——自动恢复的尝试可能是徒劳且有害的(导致活锁)。
锁死是等待线程由于某种原因一直无法被唤醒而导致其任务无法进展的一种活性故障。信号丢失锁死是由于没有相应的通知线程来唤醒等待线程而使等待线程一直处于等待状态的一种活性故障。嵌套监视器锁死是嵌套锁导致通知线程无法获得其为唤醒等待线程所需的锁从而使其无法唤醒等待线程,最终使得通知线程与等待线程都一直处于等待状态的一种活性故障。嵌套监视器锁死可以通过查看线程转储进行检测。为规避嵌套监视器锁死,我们应该避免在嵌套锁的内层临界区内实现等待/通知。
线程饥饿指线程一直无法获得其所需的资源而导致其任务一直无法进展的一种活性故障。把锁看成一种资源,那么死锁可被看作一种线程饥饿。饥饿可能演变成活锁。
活锁是线程一直在做无用功而使其任务一直无法进展的一种活性故障。试图进行死锁故障恢复可能导致活锁。