processWorkerExit(处理工作线程退出)在runWorker中,如果getTask方法没有拿到任务返回了null或者任务在执行时抛出了异常就会在最终的finally块中调用processWorkerExit方法,令当前工作线程销毁退出 。
- processWorkerExit方法内会将当前线程占用的一些资源做清理,比如从workers中移除掉当前线程(利于Worker对象的GC),并令当前线程workerCount减一(completedAbruptly=true,说明是中断导致的退出,getTask中没来得及减workerCount,在这里补正)
- completedAbruptly=true,说明是runWorker中任务异常导致的线程退出,无条件的通过addWorker重新创建一个新的工作线程代替当前退出的工作线程 。
- completedAbruptly=false,在退出当前工作线程后,需要判断一下退出后当前所存活的工作线程数量是否满足要求 。比如allowCoreThreadTimeOut=false时,当前工作线程个数是否不低于corePoolSize等,如果不满足要求则通过addWorker重新创建一个新的线程 。
- processWorkerExit方法执行完毕后,当前工作线程就完整的从当前线程池中退出了(workers中没有了引用,workerCount减1了),GC便会将内存中的Worker对象所占用的内存给回收掉 。
- 同时runWorker中最后执行完processWorkerExit后,工作线程的run方法也return了,标识着整个线程正常退出了,操作系统层面上也会将线程转为终止态并最终回收 。至此,线程占用的所有资源就被彻底的回收干净了 。
/*** 处理worker线程退出* @param myWorker 需要退出的工作线程对象* @param completedAbruptly 是否是因为中断异常的原因,而需要回收* */private void processWorkerExit(MyWorker myWorker, boolean completedAbruptly) {if (completedAbruptly) {// 如果completedAbruptly=true,说明是任务在run方法执行时出错导致的线程退出// 而正常退出时completedAbruptly=false,在getTask中已经将workerCount的值减少了decrementWorkerCount();}ReentrantLock mainLock = this.mainLock;mainLock.lock();try {// 线程池全局总完成任务数累加上要退出的工作线程已完成的任务数this.completedTaskCount += myWorker.completedTasks;// workers集合中将当前工作线程剔除workers.remove(myWorker);// completedTaskCount是long类型的,workers是HashSet,// 都是非线程安全的,所以在mainLock的保护进行修改} finally {mainLock.unlock();}int currentCtl = this.ctl.get();if (!completedAbruptly) {// completedAbruptly=false,说明不是因为中断异常而退出的// min标识当前线程池允许的最小线程数量// 1 如果allowCoreThreadTimeOut为true,则核心线程也可以被销毁,min=0// 2 如果allowCoreThreadTimeOut为false,则min应该为所允许的核心线程个数,min=corePoolSizeint min = allowCoreThreadTimeOut ? 0 : corePoolSize;if (min == 0 && ! workQueue.isEmpty()) {// 如果min为0了,但工作队列不为空,则修正min=1,因为至少需要一个工作线程来将工作队列中的任务消费、处理掉min = 1;}if (workerCountOf(currentCtl) >= min) {// 如果当前工作线程数大于了min,当前线程数量是足够的,直接返回(否则要执行下面的addWorker恢复)return;}}// 两种场景会走到这里进行addWorker操作// 1 completedAbruptly=true,说明线程是因为中断异常而退出的,需要重新创建一个新的工作线程// 2 completedAbruptly=false,且上面的workerCount<min,则说明当前工作线程数不够,需要创建一个// 为什么参数core传的是false呢?// 因为completedAbruptly=true而中断退出的线程,无论当前工作线程数是否大于核心线程,都需要创建一个新的线程来代替原有的被退出的线程addWorker(null, false);}动态修改配置参数ThreadPoolExecutor除了支持启动前通过构造函数设置配置参数外,也允许在线程池运行的过程中动态的更改配置 。而要实现动态的修改配置,麻烦程度要比启动前静态的指定大得多 。举个例子,在线程池的运行过程中如果当前corePoolSize=20,且已经创建了20个核心线程时(workerCount=20),现在将corePoolSize减少为10或者增大为30时应该如何实时的生效呢?下面通过内嵌于代码中的注释,详细的说明了allowCoreThreadTimeOut、corePoolSize、maximumPoolSize这三个关键配置参数实现动态修改的原理 。
经验总结扩展阅读
- 如何保护环境
- 哥哥给弟弟的生日祝福句子
- 送给自己的生日快乐句子
- 闺蜜生日快乐的祝福语
- 哪些星座善于伪装自己 不会轻易透露隐私
- 廉租房能自己装修吗 廉租房申请下来可以不住吗
- 申请廉租房需要查自己的个人征信吗 公租房与廉租房的区别在哪里
- 廉租房需要摇号吗 廉租房需要自己装修吗
- 自己手剥核桃仁怎么储存
- 我的Vue之旅 10 Gin重写后端、实现页面详情页 Mysql + Golang + Gin
