Springboot2整合线程池@Async异步应用场景
程序员文章站
2022-07-12 11:12:06
...
一.为何要使用异步?
1.在许多业务场景中,比如你的代码中有3个查询不同数据的方法或者去其他接口需要时间去调用,那么ABC3个接口,不是异步的情况,肯定是需要你执行A完成之后,再执行B,然后再执行C方法。那么用的时长就会很久了。所以使用异步+多线程来实现请求的时候会有3个线程去执行ABC3个方法。那么实际的时间将会缩短。
2.@Async有2种返回机制。
无返回值类型(调用直接返回,不会在意此方法执行用时,直接返回结果给客户端)
有返回值类型(返回类型为Future<T>类型或者他的子类,因为需要他来里面的方法来判断方法是否执行完成了。)
异步的话,比如发送短信,我们平时发短信都是,点击发送立马就有发送成功的提升了,但是在代码中肯定处理没有这么快,所以在方法中使用了@Async直接返回结果了,如果短信业务出现问题,可以根据业务来给你提示未送达等信息。
3.需要返回值的就需要调用Future<T>中的方法去判断是否执行完成和获取返回值等一系列操作。
二.添加pom文件
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.5.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
三.创建自定义线程池的配置类
@Configuration
@EnableAsync //开启@Async注解
public class AsyncConfiguration {
// 声明一个线程池(并指定线程池的名字,默认是方法名称)
@Bean("taskExecutor")
public Executor taskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
//核心线程数5:线程池创建时候初始化的线程数
executor.setCorePoolSize(5);
//最大线程数5:线程池最大的线程数,只有在缓冲队列满了之后才会申请超过核心线程数的线程
executor.setMaxPoolSize(5);
//缓冲队列大小:用来缓冲执行任务的队列
executor.setQueueCapacity(500);
//允许线程的空闲时间60秒:当超过了核心线程出之外的线程在空闲时间到达之后会被销毁
executor.setKeepAliveSeconds(60);
//线程池名的前缀:设置好了之后可以方便我们定位处理任务所在的线程池
executor.setThreadNamePrefix("线程名-");
//不在新线程中执行任务,而是用调用者所在的线程来执行
// executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
//执行初始化
executor.initialize();
return executor;
}
}
四.编写service
@Service
public class BmtOrderThread {
@Async("taskExecutor")
public Future<Map<String,Object>> test1() {
Map<String,Object> result=new HashMap<String,Object>();
AsyncResult<Map<String, Object>> asyncResult = new AsyncResult<>(result);
// get方法:获取计算结果(如果还没计算完,也是必须等待的)
//cancel方法:还没计算完,可以取消计算过程
//isDone方法:判断是否执行完成
// isCancelled方法:判断计算是否被取消
return new AsyncResult<>(result);
}
@Async("taskExecutor")
public Future<Boolean> test2()throws Exception {
System.out.println("开始..........test2"+Thread.currentThread().getName());
Thread.sleep(5000);
System.out.println("结束..........test2");
return new AsyncResult<>(true);
}
@Async("taskExecutor")
public Future<Boolean> test3()throws Exception {
System.out.println("开始..........test3"+Thread.currentThread().getName());
Thread.sleep(5000);
System.out.println("结束..........test3");
return new AsyncResult<>(true);
}
//异步调用,有返回值,必须是Future类型,不然报错
@Async("taskExecutor")
public Future<String> getTotalHitsFuture() {
System.out.println("开始执行");
Future<String> future;
try {
System.out.println("正在请求短信接口 . ");
Thread.sleep(1000);
System.out.println("正在发送!");
int a=12/0;
System.out.println("发送完成!");
future=new AsyncResult<>("success");
} catch (Exception e) {
System.out.println("发送失败出现异常:"+e);
future=new AsyncResult<>("error");
}
return future;
}
五.测试类
@RunWith(SpringRunner.class)
@SpringBootTest
public class SpringbootMultithreadingApplicationTests {
@Autowired
BmtOrderThread bmtOrderThread;
@Test
public void test() {
Future<String> future = bmtOrderThread.getTotalHitsFuture();
try {
Thread.sleep(1000);
String s = future.get();
if ("success".equals(s)){
System.out.println("短信发送成功之后的业务");
}else{
System.out.println("发送失败之后的业务");
}
} catch (Exception e) {
System.out.println(e);
}
}
@Test
public void test2(){
try {
long start = System.currentTimeMillis();
Future<Boolean> a = bmtOrderThread.test2();
Future<Boolean> b = bmtOrderThread.test3();
while (!a.isDone() || !b.isDone()) {
if (a.isDone() && b.isDone()) {
break;
}
}
long end = System.currentTimeMillis();
String times = "任务全部完成,总耗时:" + (end - start) + "毫秒";
System.out.println(times);
} catch (Exception e) {
e.printStackTrace();
}
}
}
6.测试test
开始执行
正在请求短信接口 .
正在发送!
发送失败出现异常:java.lang.ArithmeticException: / by zero
发送失败之后的业务
测试test2
开始..........test2线程名-1
开始..........test3线程名-2
结束..........test3
结束..........test2
任务全部完成,总耗时:5007毫秒