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应用 |
| 消息队列 | ✔️(间接) | 自动 | 高 | 大 | 分布式系统 |
选择建议
- 简单任务:优先考虑Runnable+线程池
- 需要结果:使用Callable+Future或CompletableFuture
- Spring项目:首选@Async注解
- 跨系统/高可靠:采用消息队列
- 新项目:推荐CompletableFuture或响应式编程(如Reactor)
演进趋势
现代Java开发中:
- 直接使用Thread/Runnable的方式逐渐减少
- CompletableFuture和响应式编程越来越普及
- 在微服务架构中,消息队列成为跨服务异步的首选
- Spring生态倾向于使用@Async和消息驱动
相关文章:
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&…...
【Easylive】视频在线人数统计系统实现详解 WebSocket 及其在在线人数统计中的应用
【Easylive】项目常见问题解答(自用&持续更新中…) 汇总版 视频在线人数统计系统实现详解 1. 系统架构概述 您实现的是一个基于Redis的视频在线人数统计系统,主要包含以下组件: 心跳上报接口:客户端定期调用以…...
tomcat 目录结构组成
文章目录 背景文件结构层级一些常用的路径 背景 现在非常多的 java web 服务部署在 linux 服务器中,我们服务器中的 tomcat 会有各种文件路径,看下它有哪些文件 文件结构层级 ├── bin/ # 核心脚本和启动文件 ├── conf/ # …...
苍穹外卖day12
课程内容 工作台 Apache POI 导出运营数据Excel报表 功能实现:工作台、数据导出 工作台效果图: 数据导出效果图: 在数据统计页面点击数据导出:生成Excel报表 1. 工作台 1.1 需求分析和设计 1.1.1 产品原型 工作台是系统运…...
Unity Final IK:下一代角色动画与物理交互的技术解析
引言:角色动画的范式转移 在传统游戏开发中,角色动画主要依赖于 前向动力学(Forward Kinematics, FK) 和预烘焙动画。然而,这种方法的局限性在开放世界、物理交互和VR等场景中愈发明显: 环境适应性差&…...
前端开发时的内存泄漏问题
目录 🔍 什么是内存泄漏(Memory Leak)?🚨 常见的内存泄漏场景1️⃣ 未清除的定时器(setInterval / setTimeout)2️⃣ 全局变量(变量未正确释放)3️⃣ 事件监听未清除4️⃣…...
【Feign】⭐️使用 openFeign 时传递 MultipartFile 类型的参数参考
💥💥✈️✈️欢迎阅读本文章❤️❤️💥💥 🏆本篇文章阅读大约耗时三分钟。 ⛳️motto:不积跬步、无以千里 📋📋📋本文目录如下:🎁🎁&a…...
Linux中动静态库的制作
1.什么是库 库是写好的现有的,成熟的,可以复⽤的代码。现实中每个程序都要依赖很多基础的底层库,不可能每个⼈的代码都从零开始,因此库的存在意义非同寻常。 本质上来说库是⼀种可执⾏代码的⼆进制形式,可以被操作系统…...
Docker部署sprintboot后端项目
创建Docker网络 docker network create icjs 部署Redis docker run -d \--network icjs \--name redis \-p 6379:6379 \redis:latest数据持久化 docker run --restartalways --network icjs -p 6379:6379 --name redis -v /opt/docker/redis/redis.conf:/etc/redis/redis.c…...
forms实现连连看
说明: forms实现连连看 效果图: step1:C:\Users\wangrusheng\RiderProjects\WinFormsApp2\WinFormsApp2\Form1.cs using System; using System.Collections.Generic; using System.Drawing; using System.Linq; using System.Windows.Forms;namespace …...
多视图几何--立体校正--Fusiello方法
1. 坐标系对齐与正交基构造 目标:构建新坐标系基向量 { e 1 , e 2 , e 3 } \{ \mathbf{e}_1, \mathbf{e}_2, \mathbf{e}_3 \} {e1,e2,e3},使成像平面共面且极线水平对齐。 (1) 基线方向 e 1 \mathbf{e}_1 e1 基线向量由左右相机光心平移向量…...
鸿蒙开发踩坑记录 - 2024S2
wrapBuilder如果想View和ObservedV2做绑定 必须要用 ComponentV2 Param 和 区别 退出两层循环 Builder的传入的参数及时是Trace修饰的也无法刷新组件 折叠屏展开后键盘无法点击 vm是公用的,组件生命周期问题导致 监听键盘高度变化失效 原因:分享面…...
【学Rust写CAD】21 2D 点(point.rs)
源码 //matrix/point.rs use std::ops::Mul; use super::algebraic_units::{Zero, One}; use super::generic::Matrix;/// 点坐标结构体 #[derive(Debug, Clone, Copy, PartialEq)] pub struct Point<X, Y>(Matrix<X, Y, One, Zero, Zero, One>);impl<X, Y>…...
0基础入门scrapy 框架,获取豆瓣top250存入mysql
一、基础教程 创建项目命令 scrapy startproject mySpider --项目名称 创建爬虫文件 scrapy genspider itcast "itcast.cn" --自动生成 itcast.py 文件 爬虫名称 爬虫网址 运行爬虫 scrapy crawl baidu(爬虫名) 使用终端运行太麻烦了,而且…...
鸿蒙NEXT小游戏开发:井字棋
1. 引言 井字棋是一款经典的两人对战游戏,简单易懂,适合各个年龄段的玩家。本文将介绍如何使用鸿蒙NEXT框架开发一个井字棋游戏,涵盖游戏逻辑、界面设计及AI对战功能。 2. 开发环境准备 电脑系统:windows 10 开发工具:…...
deep-sync开源程序插件导出您的 DeepSeek 与 public 聊天
一、软件介绍 文末提供下载 deep-sync开源程序插件导出您的 DeepSeek 与 public 聊天,这是一个浏览器扩展,它允许用户公开、私下分享他们的聊天对话,并使用密码或过期链接来增强 Deepseek Web UI。该扩展程序在 Deepseek 界面中添加了一个 “…...
4. 理解Prompt Engineering:如何让模型听懂你的需求
引言:当模型变成“实习生” 想象一下,你新招的实习生总把“帮我写份报告”理解为“做PPT”或“整理数据表”——这正是开发者与大模型对话的日常困境。某金融公司优化提示词后,合同审查准确率从72%飙升至94%。本文将用3个核心法则+5个行业案例,教你用Prompt Engineering让…...
网络编程—网络概念
目录 1 网络分类 1.1 局域网 1.2 广域网 2 常见网络概念 2.1 交换机 2.2 路由器 2.3 集线器 2.4 IP地址 2.5 端口号 2.6 协议 3 网络协议模型 3.1 OSI七层模型 3.2 TCP/IP五层模型 3.3 每层中常见的协议和作用 3.3.1 应用层 3.3.2 传输层 3.3.3 网络层 3.3.4…...
基于Rust与WebAssembly实现高性能前端计算
引言 随着Web应用的复杂性增加,前端开发者经常面临性能瓶颈。传统JavaScript在处理密集型计算任务(如大数据处理或实时图像渲染)时,往往显得力不从心。而Rust语言凭借其高性能和内存安全特性,结合WebAssembly的接近原生…...
MATLAB 代码学习
1. Cell数组 Cell数组用于存储异构数据,每个元素(称为cell)可以包含不同类型的数据(如数值、字符串、矩阵等)。 1.1 创建Cell数组 直接赋值:使用花括号{}定义内容。 students {Alice, 20, [85, 90, 78…...
SELinux
一、selinux技术详解 SELinux 概述 SELinux,即 Security-Enhanced Linux,意为安全强化的 Linux,由美国国家安全局(NSA)主导开发。开发初衷是防止系统资源被误用。在 Linux 系统中,系统资源的访问均通过程…...
Axios 相关的面试题
在跟着视频教程学习项目的时候使用了axios发送请求,但是只是跟着把代码粘贴上去,一些语法规则根本不太清楚,但是根据之前的博客学习了fetch了之后,一看axios的介绍就明白了。所以就直接展示axios的面试题吧 本文主要内容ÿ…...
Spring Cloud 跨云灾备:如何实现5分钟级区域切换?
引言:云原生时代,区域级故障的致命性与应对 在混合云与多云架构中,单个区域的宕机可能导致全局服务瘫痪(如2023年AWS美东区域故障影响超200家金融系统)。传统灾备方案依赖手动切换DNS或冷备集群,恢复时间长…...
ES6对函数参数的新设计
ES6 对函数参数进行了新的设计,主要添加了默认参数、不定参数和扩展参数: 不定参数和扩展参数可以认为恰好是相反的两个模式,不定参数是使用数组来表示多个参数,扩展参数则是将多个参数映射到一个数组。 需要注意:不定…...
爬虫【feapder框架】
feapder框架 1、简单介绍 简介 feapder上手简单、功能强大的Python爬虫框架,内置AirSpider、Spider、Task、Spider、BatchSpider四种爬虫解决不同场景的需求支持断点续爬、监控报警、浏览器渲染、海量数据去重等功能更有功能强大的爬虫管理系统feaplat为其提供方…...
python如何提取html中所有的图片链接
在Python中,你可以使用BeautifulSoup库来解析HTML内容,并提取其中所有的图片链接(即<img>标签的src属性)。以下是一个示例代码,展示了如何做到这一点: 首先,确保你已经安装了BeautifulSo…...
网络协议之系列
网络协议之基础介绍 。 网络协议之清空购物车时都发生了啥? 。...
LLaMA Factory微调后的大模型在vLLM框架中对齐对话模版
LLaMA Factory微调后的大模型Chat对话效果,与该模型使用vLLM推理架构中的对话效果,可能会出现不一致的情况。 下图是LLaMA Factory中的Chat的对话 下图是vLLM中的对话效果。 模型回答不稳定:有一半是对的,有一半是无关的。 1、未…...
群体智能优化算法-鹈鹕优化算法(Pelican Optimization Algorithm, POA,含Matlab源代码)
摘要 鹈鹕优化算法(Pelican Optimization Algorithm, POA)是一种灵感来自自然界鹈鹕觅食行为的元启发式优化算法。POA 模拟鹈鹕捕食的两个主要阶段:探索阶段和开发阶段。通过模拟鹈鹕追捕猎物的动态行为,该算法在全局探索和局部开…...
代理模式-spring关键设计模式,bean的增强,AOP的实现
以下是一个结合代理模式解决实际问题的Java实现案例,涵盖远程调用、缓存优化、访问控制等场景,包含逐行中文注释: 场景描述 开发一个跨网络的文件查看器,需实现: 远程文件访问:通过代理访问网络文件 缓存…...
