驽马十驾 驽马十驾

驽马十驾,功在不舍

目录
Java线程池的综合知识点
/  

Java线程池的综合知识点

本文属于对Java线程池的综合思考,如果您对Java线程池还不太了解,可以先收藏。

本文讲解如下几个内容:

  • 当核心线程满时,先创建临时线程还是先加入队列?
  • 临时线程工作时间和方式?
  • 核心线程和临时线程获取活的方式?
  • 只有临时线程才能被销毁吗?
  • 何时触发拒绝策略?
  • 线程池如何调优?

题外话:做技术这一行,一定要沉的住气,把细节想透。

先创建临时线程还是先加入队列

这里要分为2种情况来看:JDK的和Tomcat(Dubbo也可以设置)的。

  • JDK中线程池是优先加入队列,当队列满了,且当前线程数量没有达到最大线程的时候,会优先创建临时线程。
  • Tomcat的线程池是优先创建临时线程,当临时线程达到最大值才会加入队列。
  • Dubbo的线程池也是可以设置为先创建临时线程,详情见官方文档。

为什么要如此设计了?我对此的理解如下所示:

Tomcat大多处理的是IO密集型任务,线程大多数时候都是在阻塞在IO操作上,无事可做。与其如此,不如索性多创建线程,让CPU把时间片分给其他线程做后续操作。基于此考虑,优先创建线程。

实际项目中,因为临时线程不好控制,大多数建议核心线程和临时线程设置成一样大小,这种情况下,二者的表现其实是一致的了。

临时线程工作时间和方式

JDK下的临时线程创建是在:新任务加入时,队列已被填满,同时核心线程数没有达到最大线程数的时候。

那么创建后临时线程如何进行工作:

  1. 临时线程会通过pool(time,unit)的方式从队列中拉活干,干完1个任务继续干下一个任务,不会马上被销毁。。
  2. 当通过pool(time,unit)超过一定时间没有拉到活干,那么该临时线程被销毁(饿死了)

这个地方有个理解误区:临时线程的创建仅仅是为了应付队列满后新加入的那个任务,其实不是的,临时线程创建后,不仅仅处理一个任务,会不提的从队列中poll活出来干。

核心线程和临时线程获取任务的方式

当某一个核心线程干完活后,会通过take阻塞的形式从任务队列中拉取任务,如果没有就会一直阻塞在那里,标准的生产者和消费者问题。

临时线程则是通过poll(time,unit)的方式拉活来干的。

只有临时线程才能被销毁吗

临时线程如果在指定的时间内没有通过poll(time,unit)拉到活干,那么就会被清理掉。

核心线程了?

核心线程默认是不允许被销毁的,不过如果通过poolExecutor.allowCoreThreadTimeOut(true);进行设置,当核心线程超过在构造函数定义的超时时间后,仍然会被销毁,官方doc说明如下。

    /**
     * Core pool size is the minimum number of workers to keep alive
     * (and not allow to time out etc) unless allowCoreThreadTimeOut
     * is set, in which case the minimum is zero.
     */
    private volatile int corePoolSize;

我感觉我应该抽时间,把官方的doc好好读读。

何时触发拒绝策略

通常情况下,我们认为只有1种,就是在:队列满了+核心线程与临时线程之后等于最大值时,又有任务加入就会触发。

但是还有一种情况,就是调用shutdown后,线程池会将正在执行的任务执行完毕后才真正退出,此时如果再加入了一个任务,也会触发拒绝策略。

线程池如何调优

以下内容肯定很多瑕疵!

这个命题太大了,完全够写一篇文章了,个人技术和见识还有限,此处只能记录一些我知道的关于核心线程数的知识。

  • 如果任务是CPU密集型,核心线程作为N+1。N为CPU核心。
  • 如果任务是IO密集型,核心线程设置为2N+1
  • 还有一个经典的公式:( 任务等待时间/任务处理时间 + 1 )* N * CPU利用率

第三个公式,其中每个参数如何获取了?任务处理时间可以通过运行多次任务取平均的方式获取,CPU利用率?任务等待时间了?还请知道的小伙伴不吝赐教。

个人感觉应该看业务形态,业务看重吞吐量还是时延,通过测试来得到。

核心线程增多,吞吐量是先上升后下降?

核心线程增多,时延保持稳定然后再下降?

上述理论个人猜想,等这段时间忙完了,好好测试下!

总结

有些时候面试官会问题,线程池有那几个参数,不需要死记硬背,通过理解来记忆。

线程池执行图

    public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler) 
骐骥一跃,不能十步。驽马十驾,功在不舍。