Java常用异步方式总结

使用建议

完整代码见https://gitee.com/pinetree-cpu/parent-demon
提供了postMan调试json文件于security-demo/src/main/resources/test_file/java-async.postman_collection.json
可导入postMan中进行调试
在这里插入图片描述

Java异步方式以及使用场景

继承Thread类

新建三个类继承Thread,以其中一个ExtThread01为例

@Slf4j
public class ExtThread01 extends Thread {public ExtThread01(String name) {super(name);}@Overridepublic void run() {log.info("execute extThread01 start {}", Thread.currentThread().getName());try {Thread.sleep(3000);} catch (InterruptedException e) {throw new RuntimeException(e);}log.info("execute extThread01 end {}", Thread.currentThread().getName());}
}

抽取测试方法

    private static void executeExtendThread() throws InterruptedException {ExtThread01 extThread01 = new ExtThread01("extThread01");ExtThread02 extThread02 = new ExtThread02("extThread02");ExtThread03 extThread03 = new ExtThread03("extThread03");extThread01.start();extThread02.start();extThread03.start();executeMainThread();}private static void executeMainThread() throws InterruptedException {log.info("main thread start {}", Thread.currentThread().getName());Thread.sleep(3000);log.info("main thread end {}", Thread.currentThread().getName());}

输出结果

2025-03-29 17:29:00.181  INFO 11864 --- [nio-8888-exec-3] c.t.s.service.impl.TestServiceImpl       : main thread start http-nio-8888-exec-3
2025-03-29 17:29:00.182  INFO 11864 --- [    extThread01] c.t.s.entity.thread.ExtThread01          : execute extThread01 start extThread01
2025-03-29 17:29:00.182  INFO 11864 --- [    extThread03] c.t.s.entity.thread.ExtThread03          : execute extThread03 start extThread03
2025-03-29 17:29:00.182  INFO 11864 --- [    extThread02] c.t.s.entity.thread.ExtThread02          : execute extThread02 start extThread02
2025-03-29 17:29:03.186  INFO 11864 --- [    extThread02] c.t.s.entity.thread.ExtThread02          : execute extThread02 end extThread02
2025-03-29 17:29:03.186  INFO 11864 --- [nio-8888-exec-3] c.t.s.service.impl.TestServiceImpl       : main thread end http-nio-8888-exec-3
2025-03-29 17:29:03.186  INFO 11864 --- [    extThread01] c.t.s.entity.thread.ExtThread01          : execute extThread01 end extThread01
2025-03-29 17:29:03.186  INFO 11864 --- [    extThread03] c.t.s.entity.thread.ExtThread03          : execute extThread03 end extThread03

实现Runnable接口

相对于继承Thread,实现Runnable可以通过继承来更好的实现逻辑复用,如下新建抽象类,在run()中封装特定的业务操作

/*** @author PineTree* @description: 抽象业务runnable* @date 2025/3/2 17:28*/
@Slf4j
public abstract class AbstractBizRunnable implements Runnable {@Overridepublic void run() {log.info("执行特定业务的通用操作");try {start();} catch (InterruptedException e) {throw new RuntimeException(e);}}protected abstract void start() throws InterruptedException;
}

新建三个子类继承AbstractBizRunnable ,如下以为例Biz01Runnable

/*** @author PineTree* @description: 业务01runnable* @date 2025/3/2 17:50*/
@Slf4j
public class Biz01Runnable extends AbstractBizRunnable {@Overrideprotected void start() throws InterruptedException {log.info("execute biz01Runnable start {}", Thread.currentThread().getName());Thread.sleep(3000);log.info("execute biz01Runnable end {}", Thread.currentThread().getName());}
}

抽取测试方法

    private void executeImplRunnable(AsyncVO asyncVO) throws InterruptedException {// 使用线程池if (asyncVO.isUsePoolFlag()) {simplePoolExecute.execute(new Biz01Runnable());simplePoolExecute.execute(new Biz02Runnable());simplePoolExecute.execute(new Biz03Runnable());} else { // 手动调用Thread biz1thread = new Thread(new Biz01Runnable(), "biz01Runnable");Thread biz2thread = new Thread(new Biz02Runnable(), "biz02Runnable");Thread biz3thread = new Thread(new Biz03Runnable(), "biz03Runnable");biz1thread.start();biz2thread.start();biz3thread.start();}executeMainThread();}

输出结果,可以看到在每一个线程中都执行了特定业务的通用操作

2025-03-29 17:36:42.202  INFO 11864 --- [nio-8888-exec-8] c.t.s.service.impl.TestServiceImpl       : main thread start http-nio-8888-exec-8
2025-03-29 17:36:42.202  INFO 11864 --- [  biz01Runnable] c.t.s.e.runnable.AbstractBizRunnable     : 执行特定业务的通用操作
2025-03-29 17:36:42.202  INFO 11864 --- [  biz02Runnable] c.t.s.e.runnable.AbstractBizRunnable     : 执行特定业务的通用操作
2025-03-29 17:36:42.202  INFO 11864 --- [  biz01Runnable] c.t.s.entity.runnable.Biz01Runnable      : execute biz01Runnable start biz01Runnable
2025-03-29 17:36:42.203  INFO 11864 --- [  biz02Runnable] c.t.s.entity.runnable.Biz02Runnable      : execute biz02Runnable start biz02Runnable
2025-03-29 17:36:42.203  INFO 11864 --- [  biz03Runnable] c.t.s.e.runnable.AbstractBizRunnable     : 执行特定业务的通用操作
2025-03-29 17:36:42.203  INFO 11864 --- [  biz03Runnable] c.t.s.entity.runnable.Biz03Runnable      : execute biz03Runnable start biz03Runnable
2025-03-29 17:36:45.206  INFO 11864 --- [  biz03Runnable] c.t.s.entity.runnable.Biz03Runnable      : execute biz03Runnable end biz03Runnable
2025-03-29 17:36:45.206  INFO 11864 --- [  biz02Runnable] c.t.s.entity.runnable.Biz02Runnable      : execute biz02Runnable end biz02Runnable
2025-03-29 17:36:45.206  INFO 11864 --- [nio-8888-exec-8] c.t.s.service.impl.TestServiceImpl       : main thread end http-nio-8888-exec-8
2025-03-29 17:36:45.206  INFO 11864 --- [  biz01Runnable] c.t.s.entity.runnable.Biz01Runnable      : execute biz01Runnable end biz01Runnable

使用CallableFutureTask

抽取测试方法

    private void executeCallableFuture() throws InterruptedException {List<CompletableFuture<String>> allFutures = new ArrayList<>();allFutures.add(CompletableFuture.supplyAsync(() -> {log.info("CALLABLE_FUTURE task 01 start");try {Thread.sleep(3000);} catch (InterruptedException e) {}log.info("CALLABLE_FUTURE task 01 end");return "task01 result";}, simplePoolExecute));allFutures.add(CompletableFuture.supplyAsync(() -> {log.info("CALLABLE_FUTURE task 02 start");try {Thread.sleep(3000);} catch (InterruptedException e) {}log.info("CALLABLE_FUTURE task 02 end");return "task02 result";}, simplePoolExecute));allFutures.add(CompletableFuture.supplyAsync(() -> {log.info("CALLABLE_FUTURE task 02 start");try {Thread.sleep(3000);} catch (InterruptedException e) {}log.info("CALLABLE_FUTURE task 02 end");return "task03 result";}, simplePoolExecute));CompletableFuture.allOf(allFutures.toArray(new CompletableFuture[0]));executeMainThread();}

测试结果

2025-03-29 17:46:01.572  INFO 11864 --- [ingThreadPool-2] c.t.s.service.impl.TestServiceImpl       : CALLABLE_FUTURE task 02 start
2025-03-29 17:46:01.572  INFO 11864 --- [ingThreadPool-3] c.t.s.service.impl.TestServiceImpl       : CALLABLE_FUTURE task 02 start
2025-03-29 17:46:01.572  INFO 11864 --- [nio-8888-exec-2] c.t.s.service.impl.TestServiceImpl       : main thread start http-nio-8888-exec-2
2025-03-29 17:46:01.572  INFO 11864 --- [ingThreadPool-1] c.t.s.service.impl.TestServiceImpl       : CALLABLE_FUTURE task 01 start
2025-03-29 17:46:04.572  INFO 11864 --- [ingThreadPool-2] c.t.s.service.impl.TestServiceImpl       : CALLABLE_FUTURE task 02 end
2025-03-29 17:46:04.572  INFO 11864 --- [nio-8888-exec-2] c.t.s.service.impl.TestServiceImpl       : main thread end http-nio-8888-exec-2
2025-03-29 17:46:04.572  INFO 11864 --- [ingThreadPool-3] c.t.s.service.impl.TestServiceImpl       : CALLABLE_FUTURE task 02 end
2025-03-29 17:46:04.588  INFO 11864 --- [ingThreadPool-1] c.t.s.service.impl.TestServiceImpl       : CALLABLE_FUTURE task 01 end

使用Spring异步方法

定义异步类

@Slf4j
@Service
public class AsyncService {@Asyncpublic CompletableFuture<String> asyncMethodWithReturn() {try {// 模拟耗时操作Thread.sleep(3000);log.info("带返回值的异步方法执行完成 - {}", Thread.currentThread().getName());return CompletableFuture.completedFuture("Hello Async Good Result");} catch (InterruptedException e) {log.error("asyncMethodWithReturn-error", e);return CompletableFuture.completedFuture(e.getMessage());}}
}

启动类添加@EnableAsync 注解

@SpringBootApplication
@MapperScan("com.tgh.securitydemo.mapper")
@EnableAsync
public class SecurityDemoApplication {public static void main(String[] args) {SpringApplication.run(SecurityDemoApplication.class, args);}}

抽取测试方法

    private void executeSpringAsync() {asyncService.asyncMethodWithReturn().whenCompleteAsync((result, ext) -> {log.info("async方法执行完毕后获取返回结果-{}", result);});int result = 1 + 1;log.info("调用 async方法后发完消息后,让我们看看 1 + 1 的结果吧:{}", result);}

测试结果

2025-03-29 17:52:54.012  INFO 11864 --- [nio-8888-exec-5] c.t.s.service.impl.TestServiceImpl       : 调用 async方法后发完消息后,让我们看看 1 + 1 的结果吧:2
2025-03-29 17:52:57.031  INFO 11864 --- [cTaskExecutor-1] c.tgh.securitydemo.service.AsyncService  : 带返回值的异步方法执行完成 - SimpleAsyncTaskExecutor-1
2025-03-29 17:52:57.034  INFO 11864 --- [nPool-worker-11] c.t.s.service.impl.TestServiceImpl       : async方法执行完毕后获取返回结果-Hello Async Good Result

基于MQ,以rabbitMQ为例

添加mq相关配置

spring:rabbitmq:host: 192.168.32.155port: 5672username: adminpassword: 123virtual-host: /
@Configuration
public class RabbitMQConfig {// 定义队列名称public static final String QUEUE_NAME = "springboot.demo.queue";// 创建一个队列@Beanpublic Queue queue() {// 参数说明: name: 队列名称; durable: 是否持久化; exclusive: 是否排他; autoDelete: 是否自动删除return new Queue(QUEUE_NAME, true, false, false);}
}

新建消费者

/*** @author PineTree* @description: 消息消费* @date 2025/3/29 14:09*/
@Component
@Slf4j
public class MessageConsumer {/*** 监听指定队列的消息* @param message 接收到的消息*/@RabbitListener(queues = RabbitMQConfig.QUEUE_NAME)public void receiveMessage(String message) {log.info("接收到了消息,等一下再消费");try {Thread.sleep(3000);} catch (InterruptedException e) {throw new RuntimeException(e);}log.info("消费了【{}】", message);}
}

封装测试方法

    private void executeRabbitMQ() {String message = "发送了";rabbitTemplate.convertAndSend(RabbitMQConfig.QUEUE_NAME, message);log.info("消息:【{}】已发送到队列【{}】", message, RabbitMQConfig.QUEUE_NAME);int result = 1 + 2;log.info("发完消息后,让我们看看 1 + 2 的结果吧:{}", result);}

测试结果

2025-03-29 17:58:34.934  INFO 11864 --- [nio-8888-exec-4] c.t.s.service.impl.TestServiceImpl       : 消息:【发送了】已发送到队列【springboot.demo.queue】
2025-03-29 17:58:34.934  INFO 11864 --- [nio-8888-exec-4] c.t.s.service.impl.TestServiceImpl       : 发完消息后,让我们看看 1 + 2 的结果吧:3
2025-03-29 17:58:34.972  INFO 11864 --- [ntContainer#0-1] c.t.s.consumer.MessageConsumer           : 接收到了消息,等一下再消费
2025-03-29 17:58:37.979  INFO 11864 --- [ntContainer#0-1] c.t.s.consumer.MessageConsumer           : 消费了【发送了】

总结

对比总结表

实现方式返回值支持线程管理复杂度适用规模典型应用场景
继承Thread手动简单异步任务
实现Runnable手动/池小-中线程池任务
Callable+Future✔️手动/池需要结果获取
Spring @Async✔️自动中-大Spring应用
消息队列✔️(间接)自动分布式系统

选择建议

  1. 简单任务:优先考虑Runnable+线程池
  2. 需要结果:使用Callable+Future或CompletableFuture
  3. Spring项目:首选@Async注解
  4. 跨系统/高可靠:采用消息队列
  5. 新项目:推荐CompletableFuture或响应式编程(如Reactor)

演进趋势

现代Java开发中:

  • 直接使用Thread/Runnable的方式逐渐减少
  • CompletableFuture和响应式编程越来越普及
  • 在微服务架构中,消息队列成为跨服务异步的首选
  • Spring生态倾向于使用@Async和消息驱动

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/75350.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【VUE3】Pinia

目录 0前言 1 手动添加Pinia 2 创建与使用仓库&#xff08;Setup Store 组合式&#xff09; 2.1 创建仓库 2.2 使用仓库数据 2.3 解构响应式数据 3 持久化插件 0前言 官网&#xff1a;Pinia | The intuitive store for Vue.js 1 手动添加Pinia 上手之后&#xff0c;可…

JVM 每个区域分别存储什么数据?

JVM&#xff08;Java Virtual Machine&#xff09;的运行时数据区&#xff08;Runtime Data Areas&#xff09;被划分为几个不同的区域&#xff0c;每个区域都有其特定的用途和存储的数据类型。以下是 JVM 各个区域存储数据的详细说明&#xff1a; 1. 程序计数器 (Program Cou…

C++中shared_ptr 是线程安全的吗?

在 C 中&#xff0c;shared_ptr 的线程安全性和实现原理可以通过以下方式通俗理解&#xff1a; 1. shared_ptr 是线程安全的吗&#xff1f; 答案&#xff1a;部分安全&#xff0c;需分场景&#xff01; 安全的操作&#xff1a; 引用计数的增减&#xff1a;多个线程同时复制或销…

什么是 CSSD?

文章目录 一、什么是 CSSD&#xff1f;CSSD 的职责 二、CSSD 是如何工作的&#xff1f;三、CSSD 为什么会重启节点&#xff1f;情况一&#xff1a;网络和存储都断联&#xff08;失联&#xff09;情况二&#xff1a;收到其他节点对自己的踢出通知&#xff08;外部 fencing&#…

arm64平台下linux访问寄存器

通用寄存器 示例&#xff1a;读取寄存器值 // 用户态程序或内核代码中均可使用 unsigned long reg_value; asm volatile ("mov %0, x10" // 将X10的值保存到reg_value变量: "r" (reg_value) ); printk("X10 0x%lx\n", reg_value);示例&…

超级好用的小软件,连接电脑和手机。

将手机变成电脑摄像头的高效工具Iriun Webcam是一款多平台软件&#xff0c;能够将手机摄像头变成电脑的摄像头&#xff0c;通过简单的设置即可实现视频会议、直播、录制等功能。它支持Windows、Mac和Linux系统&#xff0c;同时兼容iOS和Android手机&#xff0c;操作简单&#x…

Mysql MIC高可用集群搭建

1、介绍 MySQL InnoDB Cluster&#xff08;MIC&#xff09;是基于 MySQL Group Replication&#xff08;MGR&#xff09;的高可用性解决方案&#xff0c;结合 MySQL Shell 和 MySQL Router&#xff0c;提供自动故障转移和读写分离功能&#xff0c;非常适合生产环境 2、部署 …

PERL开发环境搭建>>Windows,Linux,Mac OS

特点 简单 快速 perl解释器直接对源代码程序解释执行,是一个解释性的语言, 不需要编译器和链接器来运行代码>>速度快 灵活 借鉴了C/C, Basic, Pascal, awk, sed等多种语言, 定位于实用性语言,既具备了脚本语言的所有功能,也添加了高级语言功能 开源.免费 没有&qu…

ubuntu改用户权限

在 Linux 系统中&#xff0c;赋予普通用户 sudo 权限可以让他们执行一些需要 root 权限的命令&#xff0c;而不需要频繁切换到 root 用户。以下是具体步骤&#xff1a; 创建用户(useradd和adduser两种方式) 首先&#xff0c;需要创建一个新的用户。可以使用 adduser 或 usera…

蓝桥杯 web 学海无涯(axios、ecahrts)版本二

答案&#xff1a; // TODO: 待补充代码// 初始化图表的数据&#xff0c;设置周视图的初始数据 option.series[0].data [180, 274, 253, 324, 277, 240, 332, 378, 101]; // 周数据&#xff08;每周的总学习时长&#xff09; option.xAxis.data ["2月第1周", "…

Java 大视界 -- Java 大数据在智慧文旅虚拟场景构建与沉浸式体验增强中的技术支撑(168)

&#x1f496;亲爱的朋友们&#xff0c;热烈欢迎来到 青云交的博客&#xff01;能与诸位在此相逢&#xff0c;我倍感荣幸。在这飞速更迭的时代&#xff0c;我们都渴望一方心灵净土&#xff0c;而 我的博客 正是这样温暖的所在。这里为你呈上趣味与实用兼具的知识&#xff0c;也…

API vs 网页抓取:获取数据的最佳方式

引言 在当今数字化时代&#xff0c;对于企业、研究人员以及开发人员而言&#xff0c;获取准确且及时的数据是大多数项目成功的关键因素。目前&#xff0c;收集网页数据主要有两种常用方法&#xff0c;即使用 API&#xff08;应用程序接口&#xff09;和网页抓取。然而&#xf…

车载以太网网络测试-25【SOME/IP-报文格式-1】

目录 1 摘要2 SOME/IP-报文格式2.1 **Service ID / 16 bits**2.2 **Method ID / Event ID / 16 bits**2.3 **Length / 32 bits**2.4 **Client ID / 16 bits**2.5 Session ID / 16 bits2.6 Protocol Version / 8 bits2.7 Interface Version / 8 bits2.8 Message Type / 8 bits2.…

Python数据可视化-第3章-图表辅助元素的定制

环境 开发工具 VSCode库的版本 numpy1.26.4 matplotlib3.10.1 ipympl0.9.7教材 本书为《Python数据可视化》一书的配套内容&#xff0c;本章为第3章-图表辅助元素的定制 本章主要介绍了图表辅助元素的定制&#xff0c;包括认识常用的辅助元素、设置坐标轴的标签、设置刻度范…

小程序30-wxml语法-声明和绑定数据

小程序页面中使用的数据均需要在Page() 方法的 data对象中进行声明定义 在将数据声明好以后&#xff0c;在 WXML 使用 Mustache 语法 ( 双大括号{{ }} ) 将变量包起来&#xff0c;从而将数据绑定 在 {{ }} 内部可以做一些简单的运算&#xff0c;支持如下几种方式: 算数运算三…

ubuntu开启黑屏现象解决

文章目录 前言一、问题描述二、解决方案1. 检查显卡驱动解决步骤&#xff1a; 2. 修复 GRUB 配置解决步骤&#xff1a; 3. 使用恢复模式解决步骤&#xff1a; 三、验证与总结 前言 在使用 Ubuntu 操作系统时&#xff0c;一些用户可能会遇到开机后屏幕黑屏的现象。这种问题可能…

Modbus TCP转Profibus DP网关接防撞雷达与PLC通讯

Modbus TCP转Profibus DP网关接防撞雷达与PLC通讯 在工业自动化领域&#xff0c;通信协议的多样性既是技术进步的体现&#xff0c;也给系统集成带来了挑战。Modbus TCP和Profibus DP是两种广泛应用于不同场景下的通信标准&#xff0c;它们各有优势但也存在着互操作性的需求。本…

分布式锁方案-Redisson

分布式锁&#xff1a;Redisson还实现了Redis文档中提到像分布式锁Lock这样的更高阶应用场景。事实上Redisson并没有不止步于此&#xff0c;在分布式锁的基础上还提供了联锁&#xff08;MultiLock&#xff09;&#xff0c;读写锁&#xff08;ReadWriteLock&#xff09;&#xff…

【AI插件开发】Notepad++ AI插件开发实践:从Dock窗口集成到功能菜单实现

一、项目背景与技术选型 在上篇文章实现"选中即问AI"功能的基础上&#xff0c;本文重点解决AI对话窗口的集成与核心功能菜单的开发。通过Notepad插件体系&#xff0c;我们将实现以下功能矩阵&#xff1a; AI交互系统&#xff1a;支持自然语言提问与任务执行代码智能…

ControlNet-Tile详解

一、模型功能与应用 1. 模型功能 ControlNet-Tile模型的主要功能是图像的细节增强和质量提升。它通过以下几个步骤实现这一目标&#xff1a; 语义分割&#xff1a;模型首先对输入的图像进行语义分割&#xff0c;识别出图像中不同的区域和对象。这一步是为了让模型理解图像的内…