SpringBoot 的异步多线程需要从 java 的多线程基础说起,可以参考 java 多线程实现的三种方式区别。SpringBoot 在此基础上进行了多次封装,所以使用起来非常方便。
ThreadPoolExecutor 是 java 的核心线程池类;Spring 对 ThreadPoolExecutor 进行二次封装形成了 ThreadPoolTaskExecutor,其中几个核心参数除了名字略有改动,核心含义没变,下面说明一下:
其中拒绝策略是线程达到某种饱和后的线程池的操作策略,总共四种:
说明一下几个核心参数和拒绝策略是怎么工作的:
1.核心线程数:m,最大线程数: n(一般 n > m),线程池队列最大容量: j,线程总数: s;
2.线程进场后(s
3.线程继续进场(m
4.线程继续进场(m+j
5.线程继续进场(s>n+j),此时触发拒绝策略;
首先配置线程池:
- @Configuration
- @EnableAsync
- public class ThreadPoolConfig {
-
- @Bean("normalThreadPool") //线程池实例名,多个线程池配置需要声明,一个线程池可有可无
- public Executor executorNormal() {
- ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
- executor.setCorePoolSize(3);
- executor.setMaxPoolSize(5);
- executor.setQueueCapacity(3);
- executor.setKeepAliveSeconds(60);
- executor.setThreadNamePrefix("NORMAL--");
- executor.setRejectedExecutionHandler(new ThreadPoolExecutor.AbortPolicy());
- executor.initialize();
- return executor;
- }
- }
其次在需要异步的方法上加 @Async 注解:
- @Slf4j
- @Service
- public class ThreadTaskService {
-
- @Async("normalThreadPool") //多个线程池配置时需指定配置实例
- public void task() {
- log.info("task start...");
- try {
- Thread.sleep(3000);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- log.info("task end...");
- }
-
- }
异步的调用跟普通 service 方法没区别:
- @Slf4j
- @RestController
- @RequestMapping("/thread")
- public class ThreadTaskController {
-
- @Autowired
- ThreadTaskService taskService;
-
- @GetMapping(value = "/start")
- public String getValue() {
- taskService.task();
- return "hello...";
- }
- }
要获取异步函数的返回值可以使用 Future,但是Future 的get方法是阻塞的,使用时需要注意。
- @Async("normalThreadPool")
- public CompletableFuture
task() { - String result = "000";
- log.info("task start...");
- try {
- Thread.sleep(5000);
- result = "333";
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- log.info("task end...");
- return CompletableFuture.completedFuture(result);
- }
- @Slf4j
- @RestController
- @RequestMapping("/thread")
- public class ThreadTaskController {
- @Autowired
- ThreadTaskService taskService;
-
- @GetMapping(value = "/start")
- public String getValue() throws ExecutionException, InterruptedException {
-
- CompletableFuture
result = taskService.task(); -
- log.info("result:{}", result.get()); // get 方法会使主线程阻塞
-
- return "hello...";
- }
- }
1. 异步方法使用static关键词修饰;
2. 缺少 @EnableAsync 注解;
3. 同一个类中,一个方法调用另外一个有@Async注解的方法(原因是@Async注解的方法,是在代理类中执行的);