对ThreadPoolTaskExecutor及其源码的观察

实验概要

在我的一个SpringBoot小项目中,用到了ThreadPoolTaskExecutor。对此,我有很多零碎的疑问,通过实践和查看源码逐一验证,记录在本文。实验中,Spring Framework版本为5.3.13。

实验对象

ThreadPoolTaskExecutor是Spring提供的线程池,底层由ThreadPoolExecutor实现,参数与JAVA线程池类似,也提供了一些封装的方法,比如:

public void setCorePoolSize(int corePoolSize) {
    synchronized(this.poolSizeMonitor) {
        if (this.threadPoolExecutor != null) {
            this.threadPoolExecutor.setCorePoolSize(corePoolSize);
        }

        this.corePoolSize = corePoolSize;
    }
}

还有

public void setKeepAliveSeconds(int keepAliveSeconds) {
    synchronized(this.poolSizeMonitor) {
        if (this.threadPoolExecutor != null) {
            this.threadPoolExecutor.setKeepAliveTime((long)keepAliveSeconds, TimeUnit.SECONDS);
        }

        this.keepAliveSeconds = keepAliveSeconds;
    }
}

实验方法

对于有直观现象的问题,使用JMeter创建并发请求,使用jstack查看线程情况;对于无直观现象的、原理性的问题,直接查看其代码了解。

核心线程数可以为0吗?

可以。

我们将核心线程数设置为0,最大线程数设置为20。

执行前

执行前

执行时

执行时

执行完成后再间隔空闲线程存活时间

执行后

可以看到,在执行任务时创建的线程在存活时间结束后全部被销毁,证明全部都是应急线程。

应急线程回收后剩下的是最初创建的几个线程吗?

不一定。

我们将核心线程数设置为5,最大线程数设置为10。

首先,创建5个并发请求

执行完毕后,创建500个并发请求

等待KeepAliveSeconds秒后

可以看到,最后剩下的线程并不是最初创建的5个。

默认的阻塞队列是什么?

视传入的任务队列的容量而定,当容量大于0为LinkedBlockingQueue,其他情况为SynchronousQueue

在不设置容量时,容量默认为2147483647。

protected BlockingQueue<Runnable> createQueue(int queueCapacity) {
    return (BlockingQueue)(queueCapacity > 0 ? new LinkedBlockingQueue(queueCapacity) : new SynchronousQueue());
}

默认的拒绝策略是什么

ThreadPoolTaskExecutor的默认拒绝策略和ThreadPoolTaskExecutor一致,为AbortPolicy,即丢弃线程任务并抛出异常。

private static final RejectedExecutionHandler defaultHandler =
    new AbortPolicy();

对ThreadPoolTaskExecutor及其源码的观察
https://blog.loststar.tech/posts/3be9af02/
作者
loststar
发布于
2022年1月4日
许可协议