【最佳实践】配置类封装-Async异步注解以及自定义线程池
效果是:能点进去看到自定义的线程池,代表指定自定义的线程池成功!
自定义Async线程池
自定义线程池
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;import java.util.concurrent.ThreadPoolExecutor;/*** 线程池配置*/
@Configuration
@Slf4j
public class ThreadPoolConfig {@Value("${asyncThreadPool.corePoolSize}")private int corePoolSize;@Value("${asyncThreadPool.maxPoolSize}")private int maxPoolSize;@Value("${asyncThreadPool.queueCapacity}")private int queueCapacity;@Value("${asyncThreadPool.keepAliveSeconds}")private int keepAliveSeconds;@Value("${asyncThreadPool.awaitTerminationSeconds}")private int awaitTerminationSeconds;@Value("${asyncThreadPool.threadNamePrefix}")private String threadNamePrefix;/*** 线程池配置* @return*/@Bean(name = "threadPoolTaskExecutor")public ThreadPoolTaskExecutor threadPoolTaskExecutor() {log.info("---------- 线程池开始加载 ----------");ThreadPoolTaskExecutor threadPoolTaskExecutor = new ThreadPoolTaskExecutor();threadPoolTaskExecutor.setCorePoolSize(corePoolSize); // 核心线程池大小threadPoolTaskExecutor.setMaxPoolSize(maxPoolSize); // 最大线程数threadPoolTaskExecutor.setQueueCapacity(keepAliveSeconds); // 队列容量threadPoolTaskExecutor.setKeepAliveSeconds(queueCapacity); // 活跃时间//threadPoolTaskExecutor.setAwaitTerminationSeconds(awaitTerminationSeconds); // 主线程等待子线程执行时间threadPoolTaskExecutor.setThreadNamePrefix(threadNamePrefix); // 线程名字前缀// RejectedExecutionHandler:当pool已经达到max-size的时候,如何处理新任务// CallerRunsPolicy:不在新线程中执行任务,而是由调用者所在的线程来执行threadPoolTaskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());threadPoolTaskExecutor.initialize(); // 初始化log.info("---------- 线程池加载完成 ----------");return threadPoolTaskExecutor;}
}
application.yml 配置
# @async 线程池配置
asyncThreadPool:corePoolSize: 10maxPoolSize: 50queueCapacity: 1000keepAliveSeconds: 60awaitTerminationSeconds: 600threadNamePrefix: asyncTask-
标准模板:优雅的写法
将配置文件(默认配置)与业务代码分离
MyAsyncThreadPoolExecutorProperties
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;@Data
@ConfigurationProperties(prefix = "async.thread.pool")
public class ThreadPoolTaskExecutorProperties {/*** 核心线程数*/private int corePoolSize = 10;/*** 最大线程数*/private int maxPoolSize = 50;/*** 队列容量*/private int queueCapacity = 1000;/*** 线程存活时间*/private int keepAliveSeconds = 60;/*** 等待终止时间*/private int awaitTerminationSeconds = 600;/*** 线程名称前缀*/private String threadNamePrefix = "asyncTask-";
}
MyAsyncThreadPoolExecutor
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;@Configuration
@EnableAsync
@EnableConfigurationProperties(ThreadPoolTaskExecutorProperties.class)
public class MyAsyncThreadPoolExecutor {private final ThreadPoolTaskExecutorProperties properties;@Autowiredpublic MyAsyncThreadPoolExecutor(ThreadPoolTaskExecutorProperties properties) {this.properties = properties;}@Bean("threadPoolTaskExecutor")public ThreadPoolTaskExecutor threadPoolExecutor() {ThreadPoolTaskExecutor threadPoolTaskExecutor = new ThreadPoolTaskExecutor();threadPoolTaskExecutor.setCorePoolSize(properties.getCorePoolSize());threadPoolTaskExecutor.setMaxPoolSize(properties.getMaxPoolSize());threadPoolTaskExecutor.setQueueCapacity(properties.getQueueCapacity());threadPoolTaskExecutor.setKeepAliveSeconds(properties.getKeepAliveSeconds());threadPoolTaskExecutor.setAwaitTerminationSeconds(properties.getAwaitTerminationSeconds());threadPoolTaskExecutor.setThreadNamePrefix(properties.getThreadNamePrefix());threadPoolTaskExecutor.initialize();return threadPoolTaskExecutor;}
}
测试使用
使用方法:可以指定或者不指定,最好指定!
/*** @Async失效情况: TODO 异步发送方案OOM问题*/@Async("threadPoolTaskExecutor")public void testSend(String site) {///notify/v1/send-code?name=http://47.98.233.38:5212/loginif (StrUtil.isBlank(site)) {site = "https://www.xdclass.net/";}// 发送验证码模拟ResponseEntity<String> forEntity = this.restTemplate.getForEntity(site, String.class);String body = forEntity.getBody();log.info(body);}
特征是:会显示用了哪个线程完成任务,但是不会呈现内部的worker执行细节(得看源码)
注解失效问题
【官方】:如下方式会使@Async失效
一、异步方法使用static修饰
二、异步类没有使用@Component注解(或其他注解)导致spring无法扫描到异步类
三、异步方法不能与被调用的异步方法在同一个类中
四、类中需要使用@Autowired或@Resource等注解自动注入,不能自己手动new对象
五、如果使用SpringBoot框架必须在启动类中增加@EnableAsync注解
成功使用的原则是:
- 因为底层是由动态代理实现的,所以需要被spring管理
- 由spring管理,所以里面的类引用都是**@Autowired或@Resource等注解⾃动注⼊**,全程都要在spring的管辖范围内(方便使用动态代理的方式来完成异步调用),即调用者也必须是spring管理范围内的组件,不允许手动new对象
- 方法要求:public、非static非共享、返回值为void、Futrue
特点:
- 底层还有一个线程池,要么自己实现,要么spring自带(超不推荐)
- 动态代理实现
其他
简单demo写法
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;@Configuration
@EnableAsync
public class MyAsyncThreadPoolExecutor {@Bean("threadPoolTaskExecutor")public ThreadPoolTaskExecutor threadPoolExecutor() {ThreadPoolTaskExecutor threadPoolTaskExecutor = new ThreadPoolTaskExecutor();threadPoolTaskExecutor.setCorePoolSize(16);threadPoolTaskExecutor.setMaxPoolSize(64);threadPoolTaskExecutor.setQueueCapacity(1024);threadPoolTaskExecutor.setKeepAliveSeconds(60);threadPoolTaskExecutor.setAwaitTerminationSeconds(600);threadPoolTaskExecutor.setThreadNamePrefix("自定义线程池-");threadPoolTaskExecutor.initialize();return threadPoolTaskExecutor;}
}