简介
本文介绍Spring的@Async的用法。@Async是用来异步执行任务的。
基础代码
正常情况下,执行两个任务是这样的:
Controller
package com.knife.example.controller;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@Slf4j
@Api(tags = "测试")
@RequestMapping("test")
@RestController
public class HelloController {
@Autowired
private HelloService helloService;
@ApiOperation("测试1")
@PostMapping("test1")
public void test() {
helloService.task1();
helloService.task2();
}
}
Service
package com.knife.example.controller;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
@Slf4j
@Component
public class HelloService {
public void task1() {
log.info("开始任务1");
long start = System.currentTimeMillis();
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
long end = System.currentTimeMillis();
log.info("完成任务1,耗时:" + (end - start) + "毫秒");
}
public void task2() {
log.info("开始任务2");
long start = System.currentTimeMillis();
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
long end = System.currentTimeMillis();
log.info("完成任务2,耗时:" + (end - start) + "毫秒");
}
}
它的结果是:是顺序执行的。
2024-07-07 15:53:00.067 INFO 1044 --- [nio-8080-exec-1] c.knife.example.controller.HelloService : 开始任务1 2024-07-07 15:53:03.067 INFO 1044 --- [nio-8080-exec-1] c.knife.example.controller.HelloService : 完成任务1,耗时:3000毫秒 2024-07-07 15:53:03.067 INFO 1044 --- [nio-8080-exec-1] c.knife.example.controller.HelloService : 开始任务2 2024-07-07 15:53:06.073 INFO 1044 --- [nio-8080-exec-1] c.knife.example.controller.HelloService : 完成任务2,耗时:3006毫秒
使用异步加快速度
假如上边task1和task2是不相关的,那完全可以同时运行,加快速度!下边就用异步来做:
1.启用异步(@EnableAsync)
在启动类上加@EnableAsync
package com.knife.example;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableAsync;
@EnableAsync
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
2.将方法标记为@Async
@Async也可以放到类上边,表示所有方法都是异步执行。
下边方法上都加了@Async。另外,为了等待任务执行结束,用了CompletableFuture。
package com.knife.example.controller;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
import java.util.concurrent.CompletableFuture;
@Slf4j
@Component
public class HelloService {
@Async
public CompletableFuture<String> task1() {
log.info("开始任务1");
long start = System.currentTimeMillis();
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
long end = System.currentTimeMillis();
log.info("完成任务1,耗时:" + (end - start) + "毫秒");
return CompletableFuture.completedFuture("任务1完成");
}
@Async
public CompletableFuture<String> task2() {
log.info("开始任务2");
long start = System.currentTimeMillis();
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
long end = System.currentTimeMillis();
log.info("完成任务2,耗时:" + (end - start) + "毫秒");
return CompletableFuture.completedFuture("任务2完成");
}
}
测试:速度变快了,因为使用了两个线程去执行。
2024-07-07 15:59:33.264 INFO 8708 --- [ task-1] c.knife.example.controller.HelloService : 开始任务1 2024-07-07 15:59:33.264 INFO 8708 --- [ task-2] c.knife.example.controller.HelloService : 开始任务2 2024-07-07 15:59:36.270 INFO 8708 --- [ task-2] c.knife.example.controller.HelloService : 完成任务2,耗时:3006毫秒 2024-07-07 15:59:36.270 INFO 8708 --- [ task-1] c.knife.example.controller.HelloService : 完成任务1,耗时:3006毫秒 2024-07-07 15:59:36.270 INFO 8708 --- [nio-8080-exec-1] c.k.example.controller.HelloController : 任务完成,总耗时:3016毫秒
Async的配置
applicaion.yml
spring:
task:
execution:
pool:
# 核心线程数。默认是8
core-size: 3
# 最大线程数。默认是整数最大值
max-size: 10
# 队列长度。默认是整数最大值
queue-capacity: 100
# 是否允许核心线程超时。默认是true
allow-core-thread-timeout: true
# 空闲线程存活时间。默认是60s
keep-alive: 60s
shutdown:
# 退出时等待线程结束。默认是false
await-termination: false
# 等待线程结束的时间
await-termination-period: 30s
# 线程名称前缀。默认是task-
thread-name-prefix: task-
自定义线程池
package com.knife.example.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.AsyncConfigurer;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import java.util.concurrent.Executor;
@Configuration
public class TaskExecutorConfig implements AsyncConfigurer {
@Override
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
taskExecutor.setCorePoolSize(3);
taskExecutor.setMaxPoolSize(10);
taskExecutor.setQueueCapacity(100);
taskExecutor.initialize();
return taskExecutor;
}
}
注意事项
@Async是动态代理实现的,如果调用同一个类里被@Async标记了的方法,需要先注入当前类,再去调用。例如:
package com.knife.example.controller;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.concurrent.CompletableFuture;
@Slf4j
@Api(tags = "测试")
@RequestMapping("test")
@RestController
public class HelloController {
@Autowired
private HelloController helloController;
@ApiOperation("测试1")
@PostMapping("test1")
public void test() {
long start = System.currentTimeMillis();
CompletableFuture<String> task1 = helloController.task1();
CompletableFuture<String> task2 = helloController.task2();
CompletableFuture.allOf(task1, task2).join();
long end = System.currentTimeMillis();
log.info("任务完成,总耗时:" + (end - start) + "毫秒");
}
@Async
public CompletableFuture<String> task1() {
log.info("开始任务1");
long start = System.currentTimeMillis();
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
long end = System.currentTimeMillis();
log.info("完成任务1,耗时:" + (end - start) + "毫秒");
return CompletableFuture.completedFuture("任务1完成");
}
@Async
public CompletableFuture<String> task2() {
log.info("开始任务2");
long start = System.currentTimeMillis();
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
long end = System.currentTimeMillis();
log.info("完成任务2,耗时:" + (end - start) + "毫秒");
return CompletableFuture.completedFuture("任务2完成");
}
}

请先 !