一、四种常见的线程池
CachedThreadPool:可缓存的线程池,该线程池中没有核心线程,非核心线程的数量为Integer.max_value,就是无限大,当有需要时创建线程来执行任务,没有需要时回收线程,适用于耗时少,任务量大的情况。
SecudleThreadPool:周期性执行任务的线程池,按照某种特定的计划执行线程中的任务,有核心线程,但也有非核心线程,非核心线程的大小也为无限大。适用于执行周期性的任务。
SingleThreadPool:只有一条线程来执行任务,适用于有顺序的任务的应用场景。
FixedThreadPool:定长的线程池,有核心线程,核心线程的即为最大的线程数量,没有非核心线程
二、JUC 包的 ExecutorService 接口创建 4 种线程池
要说ThreadPoolTaskExecutor这个类,先要从java本身的JUC包讲起,我们首先要知道这一点,java中所有对线程池的管理都要遵循一个接口即ExecutorService,这个接口为我们使用线程池定义了一些规则。
在JUC(java.util.concurrent)包中为我们提供了Executors的静态方法创建几种不同的线程池来使用
/**
创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。
*/
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
/**
创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。
*/
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
/**
创建一个定长线程池,支持定时及周期性任务执行。
*/
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
return new ScheduledThreadPoolExecutor(corePoolSize);
}
@see java.util.concurrent.ScheduledThreadPoolExecutor
public ScheduledThreadPoolExecutor(int corePoolSize) {
super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
new DelayedWorkQueue());
}
@see java.util.concurrent.ThreadPoolExecutor
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), defaultHandler);
}
/**
创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。
*/
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
从源码上可以看出,这些创建线程池的方式最终都是调用了ThreadPoolExecutor类的构造方法返回了一个ExecutorService实例。
这里的ThreadPoolExecutor是一个比较重要的线程池实现类
public class ThreadPoolExecutor extends AbstractExecutorService {
...
// runState is stored in the high-order bits
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;
...
private volatile int corePoolSize;
private volatile int maximumPoolSize;
private volatile long keepAliveTime;
private static final RejectedExecutionHandler defaultHandler =new AbortPolicy();
private final BlockingQueue<Runnable> workQueue;
...
}
ThreadPoolExecutor类中定义了我们所熟悉的线程池的生命周期,还有几个核心的参数;回到正题,ThreadPoolExecutor跟我们所要讲的ThreadPoolTaskExecutor有何关系呢?上面说到,ThreadPoolExecutor是JUC包中提供给我们管理和使用线程池的实现类,且常常使用Executor工具类的几种静态方法进行实例化使用,但是请注意我们在使用的时候并不是想用线程池就使用诸如Executors.newCachedThreadPool()来创建一个的,这样已经违背线程池出现的原因,即管理宝贵的线程资源。
三、Spring集成的ThreadPoolTaskExecutor —— 单例
Spring框架中为我们封装了一个类,就是今天的主角ThreadPoolTaskExecutor,实际在使用线程池时我们就使用这个类实现,看源码:
public class ThreadPoolTaskExecutor extends ExecutorConfigurationSupport implements AsyncListenableTaskExecutor, SchedulingTaskExecutor {
private final Object poolSizeMonitor = new Object();
private int corePoolSize = 1;
private int maxPoolSize = 2147483647;
private int keepAliveSeconds = 60;
private int queueCapacity = 2147483647;
private boolean allowCoreThreadTimeOut = false;
@Nullable
private TaskDecorator taskDecorator;
@Nullable
private ThreadPoolExecutor threadPoolExecutor;
}
可以看到ThreadPoolTaskExecutor类中维护了一个ThreadPoolExecutor成员变量,此中的端倪就在这里的ThreadPoolExecutor变量。那ThreadPoolTaskExecutor实际开发中是怎么使用的呢?
1、步骤一、配置Bean
@Configuration
public class ThreadPoolTaskExecutorConfig {
@Bean("threadPoolTaskExecutor")
ThreadPoolTaskExecutor getThreadPoolTaskExecutor(){
ThreadPoolTaskExecutor threadPoolTaskExecutor = new ThreadPoolTaskExecutor();
threadPoolTaskExecutor.setCorePoolSize(3);
threadPoolTaskExecutor.setMaxPoolSize(10);
threadPoolTaskExecutor.setQueueCapacity(200);
threadPoolTaskExecutor.setThreadNamePrefix("task-concurrent-work");
threadPoolTaskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
// 这一步是不需要的,下面会讲解到
// threadPoolTaskExecutor.initialize();
return threadPoolTaskExecutor;
}
}
2、步骤二、使用时直接使用@AutoWired注解注入使用
@Autowired
ThreadPoolTaskExecutor threadPoolTaskExecutor;
...
threadPoolTaskExecutor.execute(() -> System.out.println("执行了线程工作"));
...
线程池ThreadPoolTaskExecutor配置说明,这篇文章也可以看看,具体使用描述。
3、上面讲到使用配置Bean的方式将ThreadPoolTaskExecutor交给spring进行实例化,在实例化中不需要进行 threadPoolTaskExecutor.initialize(); 这一步操作,为什么呢?
通过看源码我们看到ThreadPoolTaskExecutor类(实际上是其父类)实现了InitializingBean接口,则在spring容器初始化时通过父类实现的afterPropertiesSet()方法最终触发ThreadPoolTaskExecutor中的initializeExecutor方法从而this.threadPoolExecutor = executor;将成员变量中的ThreadPoolExecutor实例化。
protected ExecutorService initializeExecutor(ThreadFactory threadFactory, RejectedExecutionHandler rejectedExecutionHandler) {
BlockingQueue<Runnable> queue = this.createQueue(this.queueCapacity);
ThreadPoolExecutor executor;
if (this.taskDecorator != null) {
executor = new ThreadPoolExecutor(this.corePoolSize, this.maxPoolSize, (long)this.keepAliveSeconds, TimeUnit.SECONDS, queue, threadFactory, rejectedExecutionHandler) {
public void execute(Runnable command) {
Runnable decorated = ThreadPoolTaskExecutor.this.taskDecorator.decorate(command);
if (decorated != command) {
ThreadPoolTaskExecutor.this.decoratedTaskMap.put(decorated, command);
}
super.execute(decorated);
}
};
} else {
executor = new ThreadPoolExecutor(this.corePoolSize, this.maxPoolSize, (long)this.keepAliveSeconds, TimeUnit.SECONDS, queue, threadFactory, rejectedExecutionHandler);
}
if (this.allowCoreThreadTimeOut) {
executor.allowCoreThreadTimeOut(true);
}
this.threadPoolExecutor = executor;
return executor;
}
另外可以看出,实际上在ThreadPoolTaskExecutor中最终还是使用了ThreadPoolExecutor的构造方法来实现一个线程池的。
综上总结一下, 使用ThreadPoolTaskExecutor线程池时,我们需要将他交给spring进行管理(默认创建一个单例),而在使用时直接注入到组件中使用即可,这样就保证不会滥用线程池了。
参考文章:https://blog.csdn.net/qq_41923982/article/details/106268653