场景说明:
为什么需要异步方法?
合理使用异步方法可以让业务接口快到飞起!
异步方法适用于逻辑与逻辑之间可以相互分割互不影响的业务中, 如生成验证码和发送验证码组成的业务, 其实无需等到真正发送成功验证码才对客户端进行响应, 可以让短信发送这一耗时操作转为异步执行, 解耦耗时操作和核心业务;
同理还有文章阅读的业务逻辑 = 查询文章详情 + 更新文章阅读量后再响应客户端, 其实也无需等到阅读量更新后才响应文章详情给客户端, 用户查看文章是主要逻辑, 而文章阅读量更新是次要逻辑, 况且阅读量就算更新失败一点数据偏差也不会影响用户阅读因此这两个数据库操作之间的一致性是较弱的, 这类都能用异步事件去优化.
所以说: 恰当的在我们的Service中加入异步方法能大大提高接口的响应速度, 提升用户体验!
同步执行(同在一个线程中):
————————————————
异步执行(开启额外线程来执行):
SpringBoot中的异步方法支持
其实, 在SpringBoot中并不需要我们自己去创建维护线程或者线程池来异步的执行方法, SpringBoot已经提供了异步方法支持注解.
@EnableAsync // 使用异步方法时需要提前开启(在启动类上或配置类上)
@Async // 被async注解修饰的方法由SpringBoot默认线程池(SimpleAsyncTaskExecutor)执行
1.多线程工具类
package com.test.business.common.thread;
import com.ruoyi.business.common.thread.exception.CustomAsyncExceptionHandler;
import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.AsyncConfigurer;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import java.util.concurrent.Executor;
/*
- 多线程-线程池
- @author wangwei
- @date 2023-08-25
*/
@EnableAsync // 开启多线程, 项目启动时自动创建
@Configuration
public class AsyncConfig implements AsyncConfigurer {
@Override
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
// 设置核心线程数
executor.setCorePoolSize(8);
// 设置最大线程数
executor.setMaxPoolSize(20);
// 设置队列大小
executor.setQueueCapacity(Integer.MAX_VALUE);
// 设置线程活跃时间(秒)
executor.setKeepAliveSeconds(60);
// 设置线程名前缀+分组名称
executor.setThreadNamePrefix("AsyncOperationThread-");
executor.setThreadGroupName("AsyncOperationGroup");
// 所有任务结束后关闭线程池
executor.setWaitForTasksToCompleteOnShutdown(true);
// 初始化
executor.initialize();
return executor;
}
@Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return new CustomAsyncExceptionHandler();
}
}
2.业务具体使用
/*
* @return 结果
*/
@Transactional(rollbackFor = Exception.class)
@Override
public int insertTbWjlz(TbWjlz tbWjlz) throws ParseException, ExecutionException, InterruptedException {
DatedifferUtil datedifferUtil = new DatedifferUtil();
Map<String, String> datediffer = datedifferUtil.datediffer(tbWjlz.getDtXfrqDate(), tbWjlz.getDtJzrqDate());
String day = datediffer.get("day");
if(Integer.parseInt(day)<=0){
throw new CustomException("结束时间不能大于开始时间,请重新选择时间!");
}
tbWjlz.setCtId(UUID.randomUUID().toString(true));
tbWjlz.setCtFunNo(FunNoConstants.NBGZ_WJLZ);
int i = tbWjlzMapper.insertTbWjlz(tbWjlz);
if (i != 1) {
throw new CustomException("" + MessageConstants.DATA_DBADD_ERR);
}
CompletableFuture<Integer> future = insertTbTask(tbWjlz.getCtId(), tbWjlz.getCtCbmjId());
//多线程异步任务处理
// 循环等待异步请求结果
int count = 0;
while (true) {
if (future.isCancelled()) {
System.out.println("异步任务取消");
break;
}
if (future.isDone()) {
count = future.get();
System.out.println("-------------------------------------------------异步发送任务数量: "+count+" 条");
break;
}
}
System.out.println("-------------------------------------------------异步发送任务成功,共发送: "+ +count+" 条");
return i;
}
3.发送待办
private CompletableFuture insertTbTask(String ctBussId, String ctCbmjId) {
try {
SysUser user = SecurityUtils.getLoginUser().getUser();
TbTask task = new TbTask();
task.setCtId(UUID.randomUUID().toString(true));
task.setCtFunNo(FunNoConstants.NBGZ_WJLZ);
task.setCtName("【文件流转任务】 " + user.getUserId() + " 指派给" + ctCbmjId + " 请签收完成!");
int taskCode = taskMapper.insertTbTask(task);
if (taskCode != 1) {
throw new CustomException("任务发送" + MessageConstants.DATA_DBADD_ERR);
}
} catch (Exception e) {
throw new CustomException(任务发送" + MessageConstants.DATA_DBADD_ERR);
}
return CompletableFuture.completedFuture(1);
}
4.控制台打印的消息