@Async
编写两个异步任务,方法上使用@Async注解,同时启动类或异步任务类上开启@EnableAsync注解
需要注意
- 要把异步任务封装在类里面,不能直接写在Controller类中
@Slf4j
@Component
public class TaskService {
@Async
public void task1(){
try {
Thread.sleep(1000);
log.info("执行了异步task1");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
@Async
public void task2(){
try {
Thread.sleep(1000);
log.info("执行了异步task1");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
@Slf4j
@RestController
public class AsyncController {
@Autowired
private TaskService taskService;
@RequestMapping("/asyncTask")
public String AsyncTask() throws ExecutionException, InterruptedException {
log.info("开始执行异步任务");
taskService.task1();
taskService.task2();
log.info("AsyncTask方法执行完毕");
return "hello";
}
}
-----------------------
从控制台结果可以看到主线程先输出了“AsyncTask方法执行完毕”,后输出两个异步线程的运行结果,证明异步线程任务开启成功
2023-03-17 11:55:52.752 INFO 9800 --- [nio-8001-exec-2] c.l.test.controller.AsyncController : 开始执行异步任务
2023-03-17 11:55:52.754 INFO 9800 --- [nio-8001-exec-2] c.l.test.controller.AsyncController : AsyncTask方法执行完毕
2023-03-17 11:55:53.776 INFO 9800 --- [ task-1] com.ligong.test.service.TaskService : 执行了异步task1
2023-03-17 11:55:54.771 INFO 9800 --- [ task-2] com.ligong.test.service.TaskService : 执行了异步task2
带有返回值的异步任务
- 带有返回值的异步任务用Future作为方法返回值
- 用AsyncResult对象返回输出结果
@Async
public Future<Long> task2(){
long startTime = System.currentTimeMillis();
try {
Thread.sleep(2000);
log.info("执行了异步task2");
} catch (InterruptedException e) {
e.printStackTrace();
}
long endTime = System.currentTimeMillis();
//返回task2异步线程的执行时间
return new AsyncResult<>(endTime-startTime);
}
@RequestMapping("/asyncTask")
public String AsyncTask() throws ExecutionException, InterruptedException {
log.info("开始执行异步任务");
taskService.task1();
//异步任务2有返回值
Future<Long> task2Time = taskService.task2();
Long time = task2Time.get();
log.info("异步线程task2执行花费时间为"+time+"毫秒");
log.info("AsyncTask方法执行完毕");
return "hello";
}
-------------------------------------
可以看到主线程需要同步等待task2异步线程的输出结果,才能继续向下执行
2023-03-17 14:31:36.468 INFO 20692 --- [nio-8001-exec-1] c.l.test.controller.AsyncController : 开始执行异步任务
2023-03-17 14:31:37.479 INFO 20692 --- [ hello1] com.ligong.test.service.TaskService : 执行了异步task1
2023-03-17 14:31:38.485 INFO 20692 --- [ hello2] com.ligong.test.service.TaskService : 执行了异步task2
2023-03-17 14:31:38.486 INFO 20692 --- [nio-8001-exec-1] c.l.test.controller.AsyncController : 异步线程task2执行花费时间为2007毫秒
2023-03-17 14:31:38.486 INFO 20692 --- [nio-8001-exec-1] c.l.test.controller.AsyncController : AsyncTask方法执行完毕
源码:
使用@Async注解开启的异步任务,是Spring自定义线程池实现的
通过源码可以看到,Spring默认的线程池核心线程数为8,最大线程数为Integer.MAX_VALUE,阻塞队列为Integer.MAX_VALUE,可能造成无限的非核心线程的创建,这是一种资源浪费
public static class Pool {
/**
* Queue capacity. An unbounded capacity does not increase the pool and therefore
* ignores the "max-size" property.
*/
private int queueCapacity = Integer.MAX_VALUE;
/**
* Core number of threads.
*/
private int coreSize = 8;
/**
* Maximum allowed number of threads. If tasks are filling up the queue, the pool
* can expand up to that size to accommodate the load. Ignored if the queue is
* unbounded.
*/
private int maxSize = Integer.MAX_VALUE;
/**
* Whether core threads are allowed to time out. This enables dynamic growing and
* shrinking of the pool.
*/
private boolean allowCoreThreadTimeout = true;
/**
* Time limit for which threads may remain idle before being terminated.
*/
private Duration keepAlive = Duration.ofSeconds(60);
}
根据Spring自动装配原理,修改Spring默认提供的线程池参数
spring:
task:
execution:
pool:
core-size: 5
max-size: 50
queue-capacity: 200 #阻塞队列最大值为200
thread-name-prefix: hello #线程前缀