多线程业务异步处理

简介: 多线程业务异步处理

场景说明:

为什么需要异步方法?
合理使用异步方法可以让业务接口快到飞起!

异步方法适用于逻辑与逻辑之间可以相互分割互不影响的业务中, 如生成验证码和发送验证码组成的业务, 其实无需等到真正发送成功验证码才对客户端进行响应, 可以让短信发送这一耗时操作转为异步执行, 解耦耗时操作和核心业务;

同理还有文章阅读的业务逻辑 = 查询文章详情 + 更新文章阅读量后再响应客户端, 其实也无需等到阅读量更新后才响应文章详情给客户端, 用户查看文章是主要逻辑, 而文章阅读量更新是次要逻辑, 况且阅读量就算更新失败一点数据偏差也不会影响用户阅读因此这两个数据库操作之间的一致性是较弱的, 这类都能用异步事件去优化.

所以说: 恰当的在我们的Service中加入异步方法能大大提高接口的响应速度, 提升用户体验!

同步执行(同在一个线程中):

————————————————
image.png
异步执行(开启额外线程来执行):
image.png

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.控制台打印的消息
image.png

相关文章
|
Dart 调度 Android开发
Flutter 95: 图解 Dart 单线程实现异步处理之 Task Queue
0 基础学习 Flutter,第九十五节:学习一下 MicroTask 和 EventTask 任务调度!
518 0
Flutter 95: 图解 Dart 单线程实现异步处理之 Task Queue
|
Dart
Flutter 93: 图解 Dart 单线程实现异步处理之 Isolate (二)
0 基础学习 Flutter,第九十三步:补充学习 Flutter Isolate 的 compute() 方式!
772 0
Flutter 93: 图解 Dart 单线程实现异步处理之 Isolate (二)
|
Dart Android开发
Flutter 92: 图解 Dart 单线程实现异步处理之 Isolate (一)
0 基础学习 Flutter,第九十二步:了解基本的 Isolate 用法 (一)!
1455 0
Flutter 92: 图解 Dart 单线程实现异步处理之 Isolate (一)
|
Dart 前端开发 编译器
Flutter 91: 图解 Dart 单线程实现异步处理之 Future (二)
0 基础学习 Flutter,第九十一步:简单学习 Future 实现异步的基本操作 async-await!
487 0
Flutter 91: 图解 Dart 单线程实现异步处理之 Future (二)
|
Dart Java
Flutter 90: 图解 Dart 单线程实现异步处理之 Future (一)
0 基础学习 Flutter,简单学习 Future 实现异步的基本操作!
1307 0
|
2月前
|
存储 消息中间件 资源调度
C++ 多线程之初识多线程
这篇文章介绍了C++多线程的基本概念,包括进程和线程的定义、并发的实现方式,以及如何在C++中创建和管理线程,包括使用`std::thread`库、线程的join和detach方法,并通过示例代码展示了如何创建和使用多线程。
53 1
C++ 多线程之初识多线程
|
2月前
|
Java 开发者
在Java多线程编程中,创建线程的方法有两种:继承Thread类和实现Runnable接口
【10月更文挑战第20天】在Java多线程编程中,创建线程的方法有两种:继承Thread类和实现Runnable接口。本文揭示了这两种方式的微妙差异和潜在陷阱,帮助你更好地理解和选择适合项目需求的线程创建方式。
26 3
|
2月前
|
Java 开发者
在Java多线程编程中,选择合适的线程创建方法至关重要
【10月更文挑战第20天】在Java多线程编程中,选择合适的线程创建方法至关重要。本文通过案例分析,探讨了继承Thread类和实现Runnable接口两种方法的优缺点及适用场景,帮助开发者做出明智的选择。
22 2
|
2月前
|
Java
Java中多线程编程的基本概念和创建线程的两种主要方式:继承Thread类和实现Runnable接口
【10月更文挑战第20天】《JAVA多线程深度解析:线程的创建之路》介绍了Java中多线程编程的基本概念和创建线程的两种主要方式:继承Thread类和实现Runnable接口。文章详细讲解了每种方式的实现方法、优缺点及适用场景,帮助读者更好地理解和掌握多线程编程技术,为复杂任务的高效处理奠定基础。
35 2
|
2月前
|
Java 开发者
Java多线程初学者指南:介绍通过继承Thread类与实现Runnable接口两种方式创建线程的方法及其优缺点
【10月更文挑战第20天】Java多线程初学者指南:介绍通过继承Thread类与实现Runnable接口两种方式创建线程的方法及其优缺点,重点解析为何实现Runnable接口更具灵活性、资源共享及易于管理的优势。
41 1