7000字+24张图带你彻底弄懂线程池(四)

pivoteic
发布于 2022-6-17 16:50
浏览
0收藏

 

五、线程是如何获取任务的以及如何实现超时的

 

上一节我们说到,线程在执行完任务之后,会继续从getTask方法中获取任务,获取不到就会退出。接下来我们就来看一看getTask方法的实现。

7000字+24张图带你彻底弄懂线程池(四)-鸿蒙开发者社区

getTask方法,前面就是线程池的一些状态的判断,这里有一行代码

boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;

 

这行代码是判断,当前过来获取任务的线程是否可以超时退出。如果allowCoreThreadTimeOut设置为true或者线程池当前的线程数大于核心线程数,也就是corePoolSize,那么该获取任务的线程就可以超时退出。

 

那是怎么做到超时退出呢,就是这行核心代码

Runnable r = timed ?
                    workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
                    workQueue.take();


会根据是否允许超时来选择调用阻塞队列workQueue的poll方法或者take方法。如果允许超时,则会调用poll方法,传入keepAliveTime,也就是构造线程池时传入的空闲时间,这个方法的意思就是从队列中阻塞keepAliveTime时间来获取任务,获取不到就会返回null;如果不允许超时,就会调用take方法,这个方法会一直阻塞获取任务,直到从队列中获取到任务位置。从这里可以看到keepAliveTime是如何使用的了。

 

所以到这里应该就知道线程池中的线程为什么可以做到空闲一定时间就退出了吧。其实最主要的是利用了阻塞队列的poll方法的实现,这个方法可以指定超时时间,一旦线程达到了keepAliveTime还没有获取到任务,那么就会返回null,上一小节提到,getTask方法返回null,线程就会退出。

 

这里也有一个细节,就是判断当前获取任务的线程是否可以超时退出的时候,如果将allowCoreThreadTimeOut设置为true,那么所有线程走到这个timed都是true,那么所有的线程,包括核心线程都可以做到超时退出。如果你的线程池需要将核心线程超时退出,那么可以通过allowCoreThreadTimeOut方法将allowCoreThreadTimeOut变量设置为true。

 

整个getTask方法以及线程超时退出的机制如图所示

7000字+24张图带你彻底弄懂线程池(四)-鸿蒙开发者社区

六、线程池的5种状态

 

线程池内部有5个常量来代表线程池的五种状态

7000字+24张图带你彻底弄懂线程池(四)-鸿蒙开发者社区

  • RUNNING:线程池创建时就是这个状态,能够接收新任务,以及对已添加的任务进行处理。
  • SHUTDOWN:调用shutdown方法线程池就会转换成SHUTDOWN状态,此时线程池不再接收新任务,但能继续处理已添加的任务到队列中任务。
  • STOP:调用shutdownNow方法线程池就会转换成STOP状态,不接收新任务,也不能继续处理已添加的任务到队列中任务,并且会尝试中断正在处理的任务的线程。
  • TIDYING:

SHUTDOWN 状态下,任务数为 0, 其他所有任务已终止,线程池会变为 TIDYING 状态。

线程池在 SHUTDOWN 状态,任务队列为空且执行中任务为空,线程池会变为 TIDYING 状态。

线程池在 STOP 状态,线程池中执行中任务为空时,线程池会变为 TIDYING 状态。

  • TERMINATED:线程池彻底终止。线程池在 TIDYING 状态执行完 terminated() 方法就会转变为 TERMINATED 状态。

 

线程池状态具体是存在ctl成员变量中,ctl中不仅存储了线程池的状态还存储了当前线程池中线程数的大小

private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));

最后画个图来总结一下这5种状态的流转

7000字+24张图带你彻底弄懂线程池(四)-鸿蒙开发者社区
其实,在线程池运行过程中,绝大多数操作执行前都得判断当前线程池处于哪种状态,再来决定是否继续执行该操作。

 

文章转自公众号:三友的java日记

标签
已于2022-6-17 16:50:46修改
收藏
回复
举报
回复
    相关推荐