前言
上一篇中講了關於TaskExecutor的一些相關知識,本篇就是實戰篇,看看異步線程使如何使用的
正文
本篇文章使用springboot 2.2.1.RELEASE
一.前奏,直接使用,無任何配置
- 啟動異步注解
在springboot啟動類上添加注解@EnableAsync
@SpringBootApplication
@EnableAsync
public class AsyncApplication {
public static void main(String[] args) {
SpringApplication.run(AsyncApplication.class, args);
}
}
- 使用@Async注解
在需要的方法上使用@Async注解:
public interface AsyncService {
@Async
void testAsync();
@Async
void testOne();
}
@Service
public class AsyncServiceImpl implements AsyncService {
private static final Logger log= LoggerFactory.getLogger(AsyncServiceImpl.class);
@Override
public void testAsync(){
log.info("========testAsync運行=========="+Thread.currentThread().getName());
}
@Override
public void testOne(){
log.info("ThreadName:===one===="+Thread.currentThread().getName());
}
}
- controller 調用
在controller層調用異步方法,注意,異步方法最好通過注入的方式調用,如果是同類方法或工具類方法,@Async可能不會取作用。
@RestController
@RequestMapping("/test")
public class TestController {
private final AsyncService asyncService;
@Autowired
public TestController(AsyncService asyncService) {
this.asyncService = asyncService;
}
@GetMapping
public String test(){
asyncService.testAsync();
asyncService.testOne();
return "SUCCESS";
}
}
- 結果
2019-12-27 15:06:30.159 INFO 14756 --- [ task-1] c.e.async.service.impl.AsyncServiceImpl : ========testAsync運行==========task-1
2019-12-27 15:06:30.160 INFO 14756 --- [ task-2] c.e.async.service.impl.AsyncServiceImpl : ThreadName:===one====task-2
- 問題
很多人到這里就差不多完事了,我第一次用springboot的異步也是這樣。其實這里還有問題:
- 為什么線程名字是
task-,而不是很多博客所說的SimpleAsyncTaskExecutor- - 為什么應用在啟動時創建了一個名為:
applicationTaskExecutor的Executor Service
- 原因
這是因為springboot版本的原因:
springboot2.1版本新加入了明為TaskExecutionAutoConfiguration的一個配置類,在沒有實現Executor的情況是會自動注入一個name為applicationTaskExecutor的ThreadPoolTaskExecutor,具體屬性可以點進去看一看,這里就不細說了,關鍵源碼如下:
@Lazy
@Bean(name = { APPLICATION_TASK_EXECUTOR_BEAN_NAME,
AsyncAnnotationBeanPostProcessor.DEFAULT_TASK_EXECUTOR_BEAN_NAME })
@ConditionalOnMissingBean(Executor.class)
public ThreadPoolTaskExecutor applicationTaskExecutor(TaskExecutorBuilder builder) {
return builder.build();
}
- 在
springboot2.1版本之前,沒有這個TaskExecutionAutoConfiguration類,所以才會是網上很多博客所說的SimpleAsyncTaskExecutor,具體的源碼在下一篇文章中講
二. ThreadPoolTaskExecutor
如何使用ThreadPoolTaskExecutor。
@Configuration
public class AsyncConfig {
@Bean(name = "threadPoolTaskExecutor")
public ThreadPoolTaskExecutor testTaskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
//線程池名的前綴:設置好了之后可以方便我們定位處理任務所在的線程池
executor.setThreadNamePrefix("courses-schedule-");
//最大線程數10:線程池最大的線程數,只有在緩沖隊列滿了之后才會申請超過核心線程數的線程
executor.setMaxPoolSize(10);
//核心線程數3:線程池創建時候初始化的線程數
executor.setCorePoolSize(3);
//緩沖隊列0:用來緩沖執行任務的隊列
executor.setQueueCapacity(5);
//允許線程的空閑時間60秒:當超過了核心線程出之外的線程在空閑時間到達之后會被銷毀
executor.setKeepAliveSeconds(60);
// 當線程池已滿,且等待隊列也滿了的時候,直接拋棄當前線程(不會拋出異常)
// executor.setRejectedExecutionHandler(new ThreadPoolExecutor.AbortPolicy());
// executor.initialize();
return executor;
}
}
注意在使用@Async注解時在注解里面帶上參數:@Async("threadPoolTaskExecutor")
三. ConcurrentTaskExecutor
如何使用ConcurrentTaskExecutor。
@Bean(name = "concurrentTaskExecutor")
public TaskExecutor concurrentTaskExecutor () {
return new ConcurrentTaskExecutor(
Executors.newFixedThreadPool(3));
}
注意在使用@Async注解時在注解里面帶上參數:@Async("concurrentTaskExecutor")
四 接口實現
AsyncConfig:這里需要實現AsyncConfigurer接口並重寫里面得接口
@Configuration
public class AsyncConfig implements AsyncConfigurer {
@Override
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
//線程池名的前綴:設置好了之后可以方便我們定位處理任務所在的線程池
executor.setThreadNamePrefix("courses-schedule-");
//最大線程數10:線程池最大的線程數,只有在緩沖隊列滿了之后才會申請超過核心線程數的線程
executor.setMaxPoolSize(10);
//核心線程數3:線程池創建時候初始化的線程數
executor.setCorePoolSize(3);
//緩沖隊列0:用來緩沖執行任務的隊列
executor.setQueueCapacity(5);
//允許線程的空閑時間60秒:當超過了核心線程出之外的線程在空閑時間到達之后會被銷毀
executor.setKeepAliveSeconds(60);
// 當線程池已滿,且等待隊列也滿了的時候,直接拋棄當前線程(不會拋出異常)
// executor.setRejectedExecutionHandler(new ThreadPoolExecutor.AbortPolicy());
// executor.initialize();
return executor;
}
@Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return new AsyncExceptionHandler();
}
}
AsyncExceptionHandler:
public class AsyncExceptionHandler implements AsyncUncaughtExceptionHandler {
@Override
public void handleUncaughtException(Throwable throwable, Method method, Object... obj) {
System.out.println("Exception Cause - " + throwable.getMessage());
System.out.println("Method name - " + method.getName());
for (Object param : obj) {
System.out.println("Parameter value - " + param);
}
}
}
這個直接使用,不用做其他操作
五 說明
- 上面列出的方式,可以根據自己的需求做出選擇,最好選擇一種
- 如果存在兩種及以上,請在使用
@Async時加上需要使用哪一個處理,如果沒有指明使用哪一種處理,最后會默認使用SimpleAsyncTaskExecutor來處理異步任務。
最后
本篇文章主要是使用方法,如何去使用springboot的異步線程。
