项目中使用线程池的心得总结
1 线程池在项目中的运用总结
1.1 背景
工作中,用到线程池的地方是同步人员工作量的功能,该功能是选择时间同步人员工作量,工作量来自禅道系统+Digiprocess 系统。
正常是发起工作量请求,业务逻辑处理,响应入库。这样存在一个很大的弊端就是多了一次请求别的系统的数据+数据库操作,进而可能会导致响应速度较慢。
使用线程池,将同步方法放入队列中,然后直接返回响应,会极大的提高接口的响应速度。
1.2 线程池的作用
-
线程复用 -
线程资源管理 -
控制操作系统的最大并发数、以保证系统高效且安全的运行
1.3 线程池核心参数
-
corePoolSize 线程池中核心的线程数量 -
maxmumPoolSize 线程池最大线程池的数量 -
keepAliveTime 当线程超过 corePoolSize,多余的空闲线程的存活时间 -
unit KeepAliveTime 的单位 -
workQueue 任务队列 -
threadFactory 线程工厂 -
handler 拒绝策略,队列满了,并且工作线程大于等于线程池的最大线程数
1.4 工作原理
1.5 三种重要的线程池
-
newFixedThreadPool 固定大小线程池,执行长期的任务 -
newSingleThreadExecutor 单个线程的线程池 -
newCachedTreadPool 可缓存的线程池 适用于短期任务
1.6 JDK 内置的拒绝策略
-
AbortPolicy 直接抛出rejectedExecutionException 异常阻止系统正常运行 -
CallerRunsPolicy 不会丢弃任务,将某些任务回调到调用者 -
DiscarOldPolicy 抛弃队列中等待最久的任务,然后把当前任务加入队列 -
DiscarPolicy 直接丢弃任务
2 SpringBoot线程池的创建
2.1 启动类配置
在SpringBoot的主程序中配置@EnableAsync
2.2 配置
@EnableAsync
@Configuration
public class TaskPoolConfig {
/**
* 默认情况下,在创建了线程池后,线程池中的线程数为0,当有任务来之后,就会创建一个线程去执行任务,
* 当线程池中的线程数目达到corePoolSize后,就会把到达的任务放到缓存队列当中;
* 当队列满了,就继续创建线程,当线程数量大于等于maxPoolSize后,开始使用拒绝策略拒绝
*/
@Value("${task.pool.corePoolSize}")
private Integer corePoolSize;//设置核心线程数
@Value("${task.pool.maxPoolSize}")
private Integer maxPoolSize;//设置最大线程数
@Value("${task.pool.keepAliveSeconds}")
private Integer keepAliveSeconds;//设置线程活跃时间(秒)
@Value("${task.pool.queueCapacity}")
private Integer queueCapacity;//设置队列容量
@Bean("taskExecutor")
public Executor taskExecutor() {
//创建一个线程池
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
// 核心线程数10:线程池创建时候初始化的线程数
executor.setCorePoolSize(corePoolSize);
// 最大线程数20:线程池最大的线程数,只有在缓冲队列满了之后才会申请超过核心线程数的线程
executor.setMaxPoolSize(maxPoolSize);
// 缓冲队列200:用来缓冲执行任务的队列
executor.setQueueCapacity(queueCapacity);
// 允许线程的空闲时间60秒:当超过了核心线程出之外的线程在空闲时间到达之后会被销毁
executor.setKeepAliveSeconds(keepAliveSeconds);
// 线程池名的前缀:设置好了之后可以方便我们定位处理任务所在的线程池
executor.setThreadNamePrefix("taskExecutor-");
// 线程池对拒绝任务的处理策略:
// 这里采用了CallerRunsPolicy策略,当线程池没有处理能力的时候,
// 该策略会直接在 execute 方法的调用线程中运行被拒绝的任务;
// 如果执行程序已关闭,则会丢弃该任务
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
return executor;
}
}
@Async会默认从线程池获取线程,当然也可以显式的指定@Async("asyncTaskExecutor")
2.3 properties配置
### 线程池
task.pool.corePoolSize=10
task.pool.maxPoolSize=20
task.pool.keepAliveSeconds=60
task.pool.queueCapacity=200
2.4 注意事项
如下方式会使@Async失效
-
异步方法使用static修饰 -
异步类没有使用@Component注解(或其他注解)导致spring无法扫描到异步类 -
异步方法不能与被调用的异步方法在同一个类中 -
类中需要使用@Autowired或@Resource等注解自动注入,不能自己手动new对象 -
如果使用SpringBoot框架必须在启动类中增加@EnableAsync注解