前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >线程池如何回收多余线程

线程池如何回收多余线程

作者头像
小土豆Yuki
发布2022-12-01 21:25:51
1.6K0
发布2022-12-01 21:25:51
举报
文章被收录于专栏:洁癖是一只狗洁癖是一只狗

线程池如何回收多余的线程的呢,首先我们要知道几个基本的知识

一:线程池状态之间的转换

状态

含义

RUNNING

线程池的初始化状态是RUNNING, 线程池处在RUNNING状态时,能够接收新任务,以及对已添加的任务进行处理

SHUTDOWN

线程池处在SHUTDOWN状态时,不接收新任务,但能处理已添加的任务,异步中断闲置的的线程,调用线程池的 shutdown() 接口时,线程池由RUNNING -> SHUTDOWN

STOP

线程池处在STOP状态时,不接收新任务,不处理已添加的任务,并且会中断正在处理的任务。用线程池的 shutdownNow() 接口时,线程池由 (RUNNING or SHUTDOWN ) -> STOP

TIDYING

当所有的任务已终止,ctl记录的”任务数量”为0,线程池会变为TIDYING状态。当线程池变为TIDYING状态时,会执行钩子函数terminated()。terminated()在ThreadPoolExecutor类中是空的,若用户想在线程池变为TIDYING时,进行相应的处理;可以通过重载terminated()函数来实现。   当线程池在SHUTDOWN状态下,阻塞队列为空并且线程池中执行的任务也为空时,就会由 SHUTDOWN -> TIDYING。  当线程池在STOP状态下,线程池中执行的任务为空时,就会由STOP -> TIDYING

TERMINATED

线程池彻底终止,就变成TERMINATED状态。 线程池处在TIDYING状态时,执行完 terminated()之后,就会由 TIDYING -> TERMINATED

二:线程池状态的值

代码语言:javascript
复制
private static final int RUNNING    = -1 << COUNT_BITS;
private static final int SHUTDOWN   =  0 << COUNT_BITS;
private static final int STOP       =  1 << COUNT_BITS;
private static final int TIDYING    =  2 << COUNT_BITS;
private static final int TERMINATED =  3 << COUNT_BITS;

三:什么时候会进行回收线程池的线程

正如上图代码所示,当获取task任务为null的时候,就会执行processWorkerExit进行回收线程,因此关键就是上面时候task=null

四:task什么时候为null,即取不到任务

如图所示,返回null的情况有两种,如下伪代码所示

代码语言:javascript
复制
第一种情况
if(状态不是running&&(状态等于stop,tidying,terminated||队列为空))
第二种情况
boolean timeOuted=false;默认值
boolean allowCoreThreadTimeOut默认是falase,代表核心线程不会超时,即核心线程不会回收true,否则所有工作线程都有可能回收
boolean timed=allowCoreThreadTimeOut||当前线程是否大于核心线程
if((当前线程数大于线程池最大线程数||(timed&&当前线程是否超时))&&(还有工作线程||队列为空)  )

五:我们按照场景回收线程池线程

未调用shutdown() ,RUNNING状态下全部任务执行完成的场景

假设此时核心线程为4,最大线程为8,一开始线程是4个,当阻塞队列满了之后,增加到8个,然后把阻塞队列搞完了,就会减少到4个(取决于allowCoreThreadTimeOut的值,这里讨论默认值false的情况,即核心线程不会超时。如果为true,工作线程可以全部销毁),我们默认allowCoreThreadTimeOut=false

第一次循环,此时线程的状态是running状态,因此条件1不能满足

进入到条件二,timeOut=false,timed=true,因此也不能满足条件二

则进入获取任务的方法,由于timed=true,则调用workQueue.poll

代码语言:javascript
复制
try {
Runnable r = timed ?
            workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
            workQueue.take();
if (r != null)
      return r;
timedOut = true;
  } catch (InterruptedException retry) {
timedOut = false;
}          

阻塞队列没有任务返回null,此时设置超时时间timedOut=true

进入第二次循环,同理条件1不能满足

进入条件二,timed=true,timedOut=true,且队列尾空,则满足,跳出循环返回null,最后这个线程就会被移除

调用shutdown()

这种场景,无论是核心线程还是非核心线程都会被回收,调用shutdown就会发送中断信号给所有的空闲线程

最终传入false,调用下面这个方法。

可以看出,在发出中断信号前,会判断当前线程是否已经中断,以及要获得工作线程的独占锁。即让当前任务执行完之后,再发送中断信号

不管怎么样,最后都会在进入getask方法中然后返回null,最终调用方法processWorkExit进行回收

然后对应工作线程回收的场景,无非就是下面几种情况

  • 任务已经全部完成,线程在阻塞等待,然后中断唤醒,进入循环,符合getTask中条件1,最终返回null,然后进行回收
  • 任务还没有执行完成,且任务很多,线程被唤醒中断的时候,阻塞队列会调用Thread.interrupted方法,判断是中断转态,抛出异常,会被try cathc掉,然后重置线程转态,再次进行循环获取任务,不影响任何事情
  • 任务还没有执行完成,但是任务不多了,比如有4个工作线程,有2个任务,其中两个工作线程阻塞,另外2个执行任务,执行完之后进入循环,符合getTask中条件1,返回null,这里至少有一条工作线程会被回收,最后会调用tryTerminate,向任意空闲线程发送中断,阻塞的线程最终都会被回收.
本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2022-11-13,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 洁癖是一只狗 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档