什么是线程池?
线程池(ThreadPool)是一种基于池化思想管理和使用线程的机制。
它是将多个线程预先存储在一个“池子”内,当有任务出现时可以避免重新创建和销毁线程所带来性能开销,
只需要从“池子”内取出相应的线程执行对应的任务即可。
线程池优势:
➢ 降低资源消耗:通过池化技术重复利用已创建的线程,降低线程创建和销毁造成的损耗。
➢ 提高响应速度:任务到达时,无需等待线程创建即可立即执行。
➢ 提高线程的可管理性:线程是稀缺资源,如果无限制创建,不仅会消耗系统资源,还会因为线程的不合理
分布导致资源调度失衡,降低系统的稳定性。使用线程池可以进行统一的分配、调优和监控。
➢ 提供更多更强大的功能:线程池具备可拓展性,允许开发人员向其中增加更多的功能。
比如延时定时线程池ScheduledThreadPoolExecutor,就允许任务延期执行或定期执行。
在《阿里巴巴Java开发手册》中规定:线程资源必须通过线程池提供,不允许在应用中自行显式创建线程。
线程池的创建
Executors
Executors为线程池工具类,相当于一个工厂类,用来创建合适的线程池,返回ExecutorService类型的线程池。
在《阿里巴巴Java开发手册》中也不允许直接使用Executors去创建线程池的。有如下方法。
➢ Executors.newFixedThreadPool:创建一个固定大小的线程池,可控制并发的线程数,超出的线程会在队列中等待;
➢ Executors.newCachedThreadPool:创建一个可缓存的线程池,若线程数超过处理所需,
缓存一段时间后会回收,若线程数不够,则新建线程;
➢ Executors.newSingleThreadExecutor:创建单个线程数的线程池,它可以保证先进先出的执行顺序;
➢ Executors.newScheduledThreadPool:创建一个可以执行延迟任务的线程池;
➢ Executors.newSingleThreadScheduledExecutor:创建一个单线程的可以执行延迟任务的线程池;
➢ Executors.newWorkStealingPool:创建一个抢占式执行的线程池(任务执行顺序不确定)【JDK 1.8 添加】。
ThreadPoolExecutor
JDK自带的线程池,
new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L,
TimeUnit.SECONDS, new SynchronousQueue<Runnable>());
ThreadPoolTaskExecutor (推荐)
ThreadPoolTaskExecutor 是 Spring 框架提供的一个线程池实现,用于管理和执行多线程任务。
它是 TaskExecutor 接口的实现,提供了在 Spring 应用程序中创建和配置线程池的便捷方式。
➢ 使用ThreadPoolTaskExecutor 时一般需配置线程池
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import java.util.concurrent.ThreadPoolExecutor;
/**
* 线程池配置类
*
* @author zhao-cj
*/
@Configuration
@EnableAsync
public class ThreadPoolTaskConfig {
/**
* 核心线程数(默认线程数)
*/
private static final int CORE_POOL_SIZE = 20;
/**
* 最大线程数
*/
private static final int MAX_POOL_SIZE = 100;
/**
* 允许线程空闲时间(单位:默认为秒)
*/
private static final int KEEP_ALIVE_TIME = 10;
/**
* 缓冲队列大小
*/
private static final int QUEUE_CAPACITY = 200;
/**
* 线程池名前缀
*/
private static final String THREAD_NAME_PREFIX = "server-async-";
@Bean
public ThreadPoolTaskExecutor taskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(CORE_POOL_SIZE);
executor.setMaxPoolSize(MAX_POOL_SIZE);
executor.setQueueCapacity(QUEUE_CAPACITY);
executor.setKeepAliveSeconds(KEEP_ALIVE_TIME);
executor.setThreadNamePrefix(THREAD_NAME_PREFIX);
// 线程池对拒绝任务的处理策略
// CallerRunsPolicy:由调用线程(提交任务的线程)处理该任务
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
// 初始化
executor.initialize();
return executor;
}
}
或者在配置文件中配置
spring:
task:
execution:
pool:
core-size: 8
max-size: 15
queue-capacity: 100
# 自动装配类为org.springframework.boot.autoconfigure.task.TaskExecutionAutoConfiguration
# 默认配置在org.springframework.boot.autoconfigure.task.TaskExecutionProperties
➢ 在使用多线程方法上标注@Async时表明调用的线程池
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
/**
*
* @author zhao-cj
*/
@Component
@Slf4j
public class AsyncTask {
@Async("taskExecutor")
public void execute() throws Exception {
log.info("开始做任务");
long start = System.currentTimeMillis();
Thread.sleep(4000);
long end = System.currentTimeMillis();
log.info("完成任务,耗时:" + (end - start) + "毫秒");
}
}
SimpleAsyncTaskExecutor
SimpleAsyncTaskExecutor也是Spring提供线程池,当使用@Async来开启多线程时,
未指定线程池情况下,默认使用的是就是 SimpleAsyncTaskExecutor。
simpleAsyncTaskExecutor的特点是,每次执行任务时,它会重新启动一个新的线程,
其并发线程的最大数量默认是concurrencyLimit取值为-1,即不启用资源节流。
线程池的七个参数详解
➢ corePoolSize:线程池中的线程数量;
➢ maximumPoolSize:线程池中的最大线程数量;
➢ keepAliveTime:当线程池线程数量超过corePoolSize时,多余的空闲线程会在多长时间内被销毁;
➢ unit:keepAliveTime的时间单位;
➢ workQueue:任务队列,被提交但是尚未被执行的任务;
➢ threadFactory:线程工厂,用于创建线程,一般情况下使用默认的,即Executors类的静态方法defaultThreadFactory();
➢ handler:拒绝策略,
AbortPolicy(默认策略):这是默认的拒绝策略,
它会抛出一个 RejectedExecutionException 异常,表示任务被拒绝执行。
CallerRunsPolicy: 当线程池已满时,将任务返回给提交任务的调用者(Caller)。
这意味着提交任务的线程会尝试执行被拒绝的任务。
DiscardPolicy: 这个策略会默默地丢弃被拒绝的任务,不会产生任何异常。
DiscardOldestPolicy: 这个策略会丢弃队列中最老的任务,然后尝试将新任务添加到队列中。
修改 @Async 配置默认线程池
在使用 @Async 时,每次都要指定自定义的线程池比较麻烦,可以直接修改其置默认线程池
import lombok.extern.slf4j.Slf4j;
import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.AsyncConfigurer;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import java.util.concurrent.Executor;
import java.util.concurrent.ThreadPoolExecutor;
/**
* 描述:异步配置
* 注意:@EnableAsync 可放在启动类上或单独的配置类
*
* @author zhao-cj
*/
@Slf4j
@Configuration
@EnableAsync
public class AsyncConfiguration implements AsyncConfigurer {
@Bean(name = "asyncPoolTaskExecutor")
public ThreadPoolTaskExecutor executor() {
ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
//核心线程数
taskExecutor.setCorePoolSize(10);
//线程池维护线程的最大数量,只有在缓冲队列满了之后才会申请超过核心线程数的线程
taskExecutor.setMaxPoolSize(100);
//缓存队列
taskExecutor.setQueueCapacity(50);
//设置线程的空闲时间,当超过了核心线程出之外的线程在空闲时间到达之后会被销毁
taskExecutor.setKeepAliveSeconds(200);
//异步方法内部线程名称
taskExecutor.setThreadNamePrefix("async-");
/**
* 当线程池的任务缓存队列已满并且线程池中的线程数目达到maximumPoolSize,如果还有任务到来就会采取任务拒绝策略
* 通常有以下四种策略:
* ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出RejectedExecutionException异常。
* ThreadPoolExecutor.DiscardPolicy:也是丢弃任务,但是不抛出异常。
* ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任务,然后重新尝试执行任务(重复此过程)
* ThreadPoolExecutor.CallerRunsPolicy:重试添加当前的任务,自动重复调用 execute() 方法,直到成功
*/
taskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
taskExecutor.initialize();
return taskExecutor;
}
/**
* 指定默认线程池
*/
@Override
public Executor getAsyncExecutor() {
return executor();
}
/**
* 在异步方法执行过程中引发异常时执行
*/
@Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return (ex, method, params) -> log.error("线程池执行任务发送未知错误, 执行方法:{}", method.getName(), ex);
}
}