本篇围绕线程池在不同场景下的使用,各种参数调优&配置,包括线程池的关闭等;
将结合场景、源码和示例代码来说明其中关键。
目录
2.3 submit(Runnable task, T result) 有"返回值"
1. 创建线程池
之前分析过使用Executors创建线程池的弊端了,它们正是通过new ThreadPoolExecutor(...)
的方式来创建的。其实我们只要通过合理配置该构造函数的参数,就能得到一个稳定可控的池了。

以上就是线程池的全参构造函数,可以看到前面两个 if() 都是参数合法性校验。
1.1 corePoolSize
定义了线程池中最少要保持的线程数,即使线程池中没有任务,核心线程也会一直存活,直到线程池被关闭;
在创建线程池后,可以通过调用 prestartAllCoreThreads()方法提前创建线程等待任务;
也可以通过设置 allowCoreThreadTimeOut(true)来控制核心线程数是否受超时配置的影响。
public void allowCoreThreadTimeOut(boolean value) {
if (value && keepAliveTime <= 0)
throw new IllegalArgumentException("Core threads must have nonzero keep alive times");
if (value != allowCoreThreadTimeOut) {
allowCoreThreadTimeOut = value;
if (value)
interruptIdleWorkers();
}
}
1.2 maximumPoolSize
允许的最大线程数,如果队列满了,并且已创建的线程数小于最大线程数时新建线程;
如果使用了无界队列的话这个参数就失效了,这点要特别注意。
1.3 workQueue
图中比较多我们只需要关注 java.util.concurrent 包下的几个就可以了。
1.3.1 ArrayBlockingQueue
基于数组结构四号线的有界阻塞队列,按照FIFO原则进行排序;内部通过ReentrantLock实现线程安全,可以通过构造方法参数fair 实现公平锁。
1.3.2 LinkedBlockingQueue
基于链表结构实现的阻塞队列,按照FIFO原则排序,当指定容量时就是有界,反之则无界;由于其入队和出队分别两把锁,所以吞吐量通常高于ArrayBlockingQueue。Executors.newFixedThreadPool()就使用了此队列。
1.3.3 SynchronousQueue
不存储任何元素的阻塞队列,适合用于两个线程之间的协作,通常用于生产者和消费者之间直接的交换;Executors.newCachedThreadPool()就使用了此队列。
其他队列不在此说了,有机会分享下并发类容器。
1.4 TimeUnit和
keepAliveTime
TimeUnit是个枚举类,表示超时的时间单位,例如毫秒、秒、分、小时、天等;
keepAliveTime标示空闲线程的最大存活时间,这两个参数是一起的。如果不设置allowCoreThreadTimeOut(true)的话只对非核心线程有作用。
如果任务很多,且每个任务执行时间较短,执行频率也很高,可以适当调大时间,提高线程利用率。相反,如果任务不多,且执行频率很低,适当调小可回收空闲线程节省资源。
1.5 ThreadFactory
线程工厂,给当前线程池创建线程的工厂类;
我们可以使用google的工具类自定义线程名称、是否守护线程等,代码及控制台打印如下:
package org.springblade.test;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import java.util.concurrent.*;
/**
* @Auther: liuzujie
* @Date: 2025/1/1 21:45
* @Desc: 测试类
*/
public class ThreadPoolTest {
//自定义线程工厂
private static ThreadFactory threadFactory = new ThreadFactor