多线程业务异步处理

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

场景说明:

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

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

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

所以说: 恰当的在我们的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 任务调度!
578 0
Flutter 95: 图解 Dart 单线程实现异步处理之 Task Queue
|
Dart
Flutter 93: 图解 Dart 单线程实现异步处理之 Isolate (二)
0 基础学习 Flutter,第九十三步:补充学习 Flutter Isolate 的 compute() 方式!
831 0
Flutter 93: 图解 Dart 单线程实现异步处理之 Isolate (二)
|
Dart Android开发
Flutter 92: 图解 Dart 单线程实现异步处理之 Isolate (一)
0 基础学习 Flutter,第九十二步:了解基本的 Isolate 用法 (一)!
1559 0
Flutter 92: 图解 Dart 单线程实现异步处理之 Isolate (一)
|
Dart 前端开发 编译器
Flutter 91: 图解 Dart 单线程实现异步处理之 Future (二)
0 基础学习 Flutter,第九十一步:简单学习 Future 实现异步的基本操作 async-await!
528 0
Flutter 91: 图解 Dart 单线程实现异步处理之 Future (二)
|
Dart Java
Flutter 90: 图解 Dart 单线程实现异步处理之 Future (一)
0 基础学习 Flutter,简单学习 Future 实现异步的基本操作!
1366 0
|
4月前
|
机器学习/深度学习 消息中间件 存储
【高薪程序员必看】万字长文拆解Java并发编程!(9-2):并发工具-线程池
🌟 ​大家好,我是摘星!​ 🌟今天为大家带来的是并发编程中的强力并发工具-线程池,废话不多说让我们直接开始。
188 0
|
7月前
|
Linux
Linux编程: 在业务线程中注册和处理Linux信号
通过本文,您可以了解如何在业务线程中注册和处理Linux信号。正确处理信号可以提高程序的健壮性和稳定性。希望这些内容能帮助您更好地理解和应用Linux信号处理机制。
131 26
|
7月前
|
Linux
Linux编程: 在业务线程中注册和处理Linux信号
本文详细介绍了如何在Linux中通过在业务线程中注册和处理信号。我们讨论了信号的基本概念,并通过完整的代码示例展示了在业务线程中注册和处理信号的方法。通过正确地使用信号处理机制,可以提高程序的健壮性和响应能力。希望本文能帮助您更好地理解和应用Linux信号处理,提高开发效率和代码质量。
135 17
|
9月前
|
存储 安全 Java
Java多线程编程秘籍:各种方案一网打尽,不要错过!
Java 中实现多线程的方式主要有四种:继承 Thread 类、实现 Runnable 接口、实现 Callable 接口和使用线程池。每种方式各有优缺点,适用于不同的场景。继承 Thread 类最简单,实现 Runnable 接口更灵活,Callable 接口支持返回结果,线程池则便于管理和复用线程。实际应用中可根据需求选择合适的方式。此外,还介绍了多线程相关的常见面试问题及答案,涵盖线程概念、线程安全、线程池等知识点。
597 2
|
10月前
|
设计模式 Java 开发者
Java多线程编程的陷阱与解决方案####
本文深入探讨了Java多线程编程中常见的问题及其解决策略。通过分析竞态条件、死锁、活锁等典型场景,并结合代码示例和实用技巧,帮助开发者有效避免这些陷阱,提升并发程序的稳定性和性能。 ####