注意事项
- 使用
@Async
注解将方法标记为异步方法
- 没有额外配置时,默认会采用 SimpleAsyncTaskExecutor 处理任务。每次调用
@Async
方法时,该执行器会创建一个新线程执行任务,而非复用线程。这就可能带来问题,要是并发请求数量很多,系统资源会被大量消耗,甚至有导致 OOM(内存溢出)的风险
- 可以通过配置
ThreadPoolTaskExecutor
来创建一个可复用线程的池,以此提升系统性能。
- 默认线程池参数(未手动配置时):
- 核心线程数(corePoolSize):8
- 最大线程数(maxPoolSize):Integer.MAX_VALUE
- 队列容量(queueCapacity):Integer.MAX_VALUE
- 空闲线程保留时间:60秒
- 可以在配置类或配置文件中修改默认线程池参数
- 异步方法必须在 public 方法中定义,并且不能在同一类中调用本类中的其他异步方法,否则其将会同步执行
配置线程池
配置类方式
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| @Configuration @EnableAsync public class AsyncConfig { @Bean public ThreadPoolTaskExecutor taskExecutor() { ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); executor.setCorePoolSize(10); executor.setMaxPoolSize(50); executor.setQueueCapacity(100); executor.setThreadNamePrefix("Async-"); executor.initialize(); return executor; } }
|
配置文件方式
Spring Boot 会读取这些配置并应用到自动创建的 ThreadPoolTaskExecutor
实例中
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| spring: task: execution: pool: max-size: 16 core-size: 16 keep-alive: 10s queue-capacity: 100 allow-core-thread-timeout: true thread-name-prefix: async-task-
|
示例代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58
| @Component @Slf4j public class TaskFactory {
@Async public Future<Boolean> asyncTask1() throws InterruptedException { doTask("asyncTask1", 5); return new AsyncResult<>(Boolean.TRUE); }
@Async public Future<Boolean> asyncTask2() throws InterruptedException { doTask("asyncTask2", 2); return new AsyncResult<>(Boolean.TRUE); }
@Async public Future<Boolean> asyncTask3() throws InterruptedException { doTask("asyncTask3", 3); return new AsyncResult<>(Boolean.TRUE); }
public void task1() throws InterruptedException { doTask("task1", 5); }
public void task2() throws InterruptedException { doTask("task2", 2); }
public void task3() throws InterruptedException { doTask("task3", 3); }
private void doTask(String taskName, Integer time) throws InterruptedException { log.info("{}开始执行,当前线程名称【{}】", taskName, Thread.currentThread().getName()); TimeUnit.SECONDS.sleep(time); log.info("{}执行成功,当前线程名称【{}】", taskName, Thread.currentThread().getName()); } }
|