具体请参考原创:
《Java线程池实现原理及其在美团业务中的实践》
《Java 线程池及参数动态调节详解》
线程的创建和销毁会造成一定的时间和空间上的消耗,线程池可以让我们重复利用已创建的线程。
线程池已为我们创建好了线程,当任务到达时可以不需要等到线程创建就能立即执行。
线程是稀缺资源,不可能无限的创建,使用线程池可以进行统一分配、调优和监控。
线程池具备可拓展性,允许开发人员向其中增加更多的功能。比如延时定时线程池ScheduledThreadPoolExecutor,就允许任务延期执行或定期执行。
1、核心参数
ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)
参数 |
说明 |
corePoolSize |
核心线程数量,线程池维护线程的最少数量 |
maximumPoolSize |
线程池维护线程的最大数量 |
keepAliveTime |
非核心线程的最长空闲时间,超过该时间的空闲线程会被销毁 |
unit |
keepAliveTime的单位,有NANOSECONDS(纳秒)、MICROSECONDS(微秒)、MILLISECONDS(毫秒)、SECONDS(秒) |
workQueue |
任务缓冲队列(阻塞队列) |
threadFactory |
线程工厂,用于创建线程,一般用默认的即可 |
handle |
线程池对拒绝任务的处理策略 |
阻塞队列:在队列为空时,获取元素的线程会等待队列变为非空。当队列满时,存储元素的线程会等待队列可用。
ThreadPoolExecutor提供了四种拒绝策略:
ThreadPoolExecutor的运行状态:
运行状态 |
状态描述 |
RUNNING |
能接受新提交的任务、并且也能处理阻塞队列中的任务 |
SHUTDOWN |
关闭状态,不再接受新提交的任务,但可以继续处理阻塞队列中已保存的任务 |
STOP |
不能接受新提及的任务,也不处理队列中的任务,会中断正在处理任务的线程 |
TIDYING |
所有的任务都已经终止了,workerCount(有效线程数)为0 |
TERMINATED |
在terminated()方法执行完后进入该状态 |
2、执行原理
1、线程池的创建方式
(1)通过Executors线程工厂类创建(不推荐)
1. Executors.newCachedThreadPool();
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0,
Integer.MAX_VALUE,
60L,
TimeUnit.SECONDS,
new SynchronousQueue());
}
该线程池存在的问题:允许的创建线程数量为 Integer.MAX_VALUE, 可能会创建大量的线程,从而导致 OOM。
2. Executors.newFixedThreadPool(int nThreads);
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads,
nThreads,
0L,
TimeUnit.MILLISECONDS,
new LinkedBlockingQueue());
}
该线程池存在的问题:允许的请求队列长度为 Integer.MAX_VALUE,可能会堆积大量的请求,从而导致 OOM。
3. Executors.newSingleThreadExecutor();
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService (
new ThreadPoolExecutor(1,
1,
0L,
TimeUnit.MILLISECONDS,
new LinkedBlockingQueue()));
}
该线程池存在的问题:允许的请求队列长度为 Integer.MAX_VALUE,可能会堆积大量的请求,从而导致 OOM。
4. Executors.newScheduledThreadPool(int corePoolSize);
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
return new ScheduledThreadPoolExecutor(corePoolSize);
}
public ScheduledThreadPoolExecutor(int corePoolSize) {
super(corePoolSize,
Integer.MAX_VALUE,
0,
NANOSECONDS,
new DelayedWorkQueue());
}
该线程池存在的问题:允许的创建线程数量为 Integer.MAX_VALUE, 可能会创建大量的线程,从而导致 OOM。
(2)通过new ThreadPoolExecutor自定义创建(推荐)
ThreadPoolExecutor pool = new ThreadPoolExecutor(5,
20,
60,
TimeUnit.SECONDS,
new LinkedBlockingQueue<>(200));
2、线程池使用规范(阿里巴巴)
3、SpringBoot项目中使用线程池
(1)配置线程池并开启异步任务
@Configuration
@EnableAsync // 开启异步任务支持
public class ExecutorConfig {
// 声明线程池
@Bean("taskExecutor")
public Executor taskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(5);
executor.setMaxPoolSize(20);
executor.setQueueCapacity(2000);
executor.setThreadNamePrefix("taskExecutor-");
executor.setRejectedExecutionHandler(new
ThreadPoolExecutor.CallerRunsPolicy());
//执行初始化
executor.initialize();
return executor;
}
}
(2)在@Async中使用自定义线程池
@Service
public class TaskService {
// @Async声明方法为异步方法并自定使用自定义线程池
@Async("taskExecutor")
public void task1() {
// 具体业务
}
}
(3)@Async失效(本质是代理没有生效)
具体参考:
《Java线程池实现原理及其在美团业务中的实践》
《Java 线程池及参数动态调节详解》
1、合理配置线程池参数(并没用通用的计算方式)
业界的一些线程池参数配置方案:
2、线程池参数动态配置&线程池监控 (美团业务中的方案)
线程池参数动态化前后的参数修改流程对比:
线程池参数动态化整体架构:
线程池参数动态化功能架构: