了解下JUC的线程池学习十一(理解可重入锁mainLock成员变量)
1.主要的变量
private final ReentrantLock mainLock = new ReentrantLock();
private final Condition termination = mainLock.newCondition();
2.ThreadPoolExecutor
内部成员属性mainLock
的引用情况
3.mainLock应用场景
第一个:tryTerminate
保证状态TIDYING -> TERMINATED
,钩子方法terminated()
回调和条件变量唤醒
第二个:interruptIdleWorkers
保护工作线程中断的串行化,避免"中断风暴"
第三个:addWorker
保护工作线程集合避免并发增加工作线程、保护度量统计数据变更
第四个:processWorkerExit
保护度量统计数据变更
第五个:shutdown
、shutdownNow
和awaitTermination
多次并发调用shutdown()
如果不加锁,会反复中断工作线程
第六个:getPoolSize
、getActiveCount
、getLargestPoolSize
、getTaskCount
和getCompletedTaskCount
保护度量统计数据读取,这些统计数据来一般源于Worker
集合的属性统计
4.分析下shutdown为何加上mainLock
public void shutdown() {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
checkShutdownAccess();
advanceRunState(SHUTDOWN);
interruptIdleWorkers();
onShutdown(); // hook for ScheduledThreadPoolExecutor
} finally {
mainLock.unlock();
}
tryTerminate();
}
说明:这里shutdown()
中除了tryTerminate()
,其他它方法都是包裹在锁里面执行,
确保工作线程集合稳定性以及关闭权限、确保状态变更串行化,
中断所有工作线程并且避免工作线程"中断风暴"(多次并发调用shutdown()
如果不加锁,会反复中断工作线程)。
public List<Runnable> shutdownNow() {
List<Runnable> tasks;
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
checkShutdownAccess();
advanceRunState(STOP);
interruptWorkers();
tasks = drainQueue(); # <--- 多了这一步
} finally {
mainLock.unlock();
}
tryTerminate();
return tasks;
}
总结: shutdownNow()
方法其实加锁的目的和shutdown()
差不多,不过多了一步:导出任务队列中的剩余的任务实例列表。
awaitTermination()
方法中使用到前面提到过的条件变量termination
:
// 条件变量必须在锁代码块中执行,和synchronized关键字用法差不多
public boolean awaitTermination(long timeout, TimeUnit unit)
throws InterruptedException {
long nanos = unit.toNanos(timeout);
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
// 死循环确保等待执行和状态变更为TERMINATED
while (runStateLessThan(ctl.get(), TERMINATED)) {
if (nanos <= 0L)
return false;
# <-- 确保当前调用线程阻塞等待对应的时间或者线程池状态变更为TERMINATED,再退出等待
nanos = termination.awaitNanos(nanos);
}
return true;
} finally {
mainLock.unlock();
}
}
总结:awaitTermination()
方法的核心功能是:确保当前调用awaitTermination()
方法
的线程阻塞等待对应的时间或者线程池状态变更为TERMINATED,再退出等待返回结果,
这样能够让使用者输入一个可以接受的等待时间进行阻塞等待,
或者线程池在其他线程中被调用了shutdown()
方法状态变更为TERMINATED
就能正常解除阻塞。
awaitTermination()
方法的返回值为布尔值,true
代表线程池状态变更为TERMINATED
或者等待了
输入时间范围内的时间周期被唤醒,意味则线程池正常退出,结果为false
代表等待了超过输入时间
范围内的时间周期,线程池的状态依然没有更变为TERMINATED
。
注 : 线程池中的工作线程如何优雅地退出,不导致当前任务执行丢失、
任务状态异常或者任务持有的数据异常,是一个很值得探讨的专题。
学习来源:https://www.cnblogs.com/throwable/p/13574306.html