线程池如何回收多余线程

2022-12-01 21:25:51 浏览数 (1)

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

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

状态

含义

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,向任意空闲线程发送中断,阻塞的线程最终都会被回收.

0 人点赞