Java常用异步方式总结

news2025/7/15 16:47:37

使用建议

完整代码见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);
    }

    @Override
    public 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 {
    @Override
    public 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 {

    @Override
    protected 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 {
    @Async
    public 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.155
    port: 5672
    username: admin
    password: 123
    virtual-host: /
@Configuration
public class RabbitMQConfig {
    // 定义队列名称
    public static final String QUEUE_NAME = "springboot.demo.queue";

    // 创建一个队列
    @Bean
    public 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.coloradmin.cn/o/2327621.html

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

相关文章

苍穹外卖day12

课程内容 工作台 Apache POI 导出运营数据Excel报表 功能实现&#xff1a;工作台、数据导出 工作台效果图&#xff1a; 数据导出效果图&#xff1a; 在数据统计页面点击数据导出&#xff1a;生成Excel报表 1. 工作台 1.1 需求分析和设计 1.1.1 产品原型 工作台是系统运…

【Feign】⭐️使用 openFeign 时传递 MultipartFile 类型的参数参考

&#x1f4a5;&#x1f4a5;✈️✈️欢迎阅读本文章❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;本篇文章阅读大约耗时三分钟。 ⛳️motto&#xff1a;不积跬步、无以千里 &#x1f4cb;&#x1f4cb;&#x1f4cb;本文目录如下&#xff1a;&#x1f381;&#x1f381;&a…

Linux中动静态库的制作

1.什么是库 库是写好的现有的&#xff0c;成熟的&#xff0c;可以复⽤的代码。现实中每个程序都要依赖很多基础的底层库&#xff0c;不可能每个⼈的代码都从零开始&#xff0c;因此库的存在意义非同寻常。 本质上来说库是⼀种可执⾏代码的⼆进制形式&#xff0c;可以被操作系统…

forms实现连连看

说明&#xff1a; forms实现连连看 效果图&#xff1a; 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 …

鸿蒙开发踩坑记录 - 2024S2

wrapBuilder如果想View和ObservedV2做绑定 必须要用 ComponentV2 Param 和 区别 退出两层循环 Builder的传入的参数及时是Trace修饰的也无法刷新组件 折叠屏展开后键盘无法点击 vm是公用的&#xff0c;组件生命周期问题导致 监听键盘高度变化失效 原因&#xff1a;分享面…

0基础入门scrapy 框架,获取豆瓣top250存入mysql

一、基础教程 创建项目命令 scrapy startproject mySpider --项目名称 创建爬虫文件 scrapy genspider itcast "itcast.cn" --自动生成 itcast.py 文件 爬虫名称 爬虫网址 运行爬虫 scrapy crawl baidu(爬虫名&#xff09; 使用终端运行太麻烦了&#xff0c;而且…

鸿蒙NEXT小游戏开发:井字棋

1. 引言 井字棋是一款经典的两人对战游戏&#xff0c;简单易懂&#xff0c;适合各个年龄段的玩家。本文将介绍如何使用鸿蒙NEXT框架开发一个井字棋游戏&#xff0c;涵盖游戏逻辑、界面设计及AI对战功能。 2. 开发环境准备 电脑系统&#xff1a;windows 10 开发工具&#xff1a;…

deep-sync开源程序插件导出您的 DeepSeek 与 public 聊天

一、软件介绍 文末提供下载 deep-sync开源程序插件导出您的 DeepSeek 与 public 聊天&#xff0c;这是一个浏览器扩展&#xff0c;它允许用户公开、私下分享他们的聊天对话&#xff0c;并使用密码或过期链接来增强 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…

SELinux

一、selinux技术详解 SELinux 概述 SELinux&#xff0c;即 Security-Enhanced Linux&#xff0c;意为安全强化的 Linux&#xff0c;由美国国家安全局&#xff08;NSA&#xff09;主导开发。开发初衷是防止系统资源被误用。在 Linux 系统中&#xff0c;系统资源的访问均通过程…

ES6对函数参数的新设计

ES6 对函数参数进行了新的设计&#xff0c;主要添加了默认参数、不定参数和扩展参数&#xff1a; 不定参数和扩展参数可以认为恰好是相反的两个模式&#xff0c;不定参数是使用数组来表示多个参数&#xff0c;扩展参数则是将多个参数映射到一个数组。 需要注意&#xff1a;不定…

LLaMA Factory微调后的大模型在vLLM框架中对齐对话模版

LLaMA Factory微调后的大模型Chat对话效果&#xff0c;与该模型使用vLLM推理架构中的对话效果&#xff0c;可能会出现不一致的情况。 下图是LLaMA Factory中的Chat的对话 下图是vLLM中的对话效果。 模型回答不稳定&#xff1a;有一半是对的&#xff0c;有一半是无关的。 1、未…

群体智能优化算法-鹈鹕优化算法(Pelican Optimization Algorithm, POA,含Matlab源代码)

摘要 鹈鹕优化算法&#xff08;Pelican Optimization Algorithm, POA&#xff09;是一种灵感来自自然界鹈鹕觅食行为的元启发式优化算法。POA 模拟鹈鹕捕食的两个主要阶段&#xff1a;探索阶段和开发阶段。通过模拟鹈鹕追捕猎物的动态行为&#xff0c;该算法在全局探索和局部开…

在 Blazor 中使用 Chart.js 快速创建数据可视化图表

前言 BlazorChartjs 是一个在 Blazor 中使用 Chart.js 的库&#xff08;支持Blazor WebAssembly和Blazor Server两种模式&#xff09;&#xff0c;它提供了简单易用的组件来帮助开发者快速集成数据可视化图表到他们的 Blazor 应用程序中。本文我们将一起来学习一下在 Blazor 中…

SQL server 2022和SSMS的使用案例1

一&#xff0c;案例讲解 二&#xff0c;实战讲解 实战环境 你需要确保你已经安装完成SQL Server 2022 和SSMS 20.2 管理面板。点此跳转至安装教程 SQL Server2022Windows11 专业工作站SSMS20.2 1&#xff0c;连接数据库 打开SSMS&#xff0c;连接数据库。 正常连接示意图&…

GO语言学习(14)GO并发编程

目录 &#x1f308;前言 1.goroutine&#x1f31f; 2.GMP模型&#x1f31f; 2.1 GMP的由来☀️ 2.2 什么是GMP☀️ 3.channel &#x1f31f; 3.1 通道声明与数据传输&#x1f4a5; 3.2 通道关闭 &#x1f4a5; 3.3 通道遍历 &#x1f4a5; 3.4 Select语句 &#x1f4…

【Audio开发二】Android原生音量曲线调整说明

一&#xff0c;客制化需求 客户方对于音量加减键从静音到最大音量十五个档位区域的音量变化趋势有定制化需求。 二&#xff0c;音量曲线调试流程 Android根据不同的音频流类型定义不同的曲线&#xff0c;曲线文件存放在/vendor/etc/audio_policy_volumes.xml或者default_volu…

spring-security原理与应用系列:HttpSecurity.filters

目录 AnyRequestMatcher WebSecurityConfig HttpSecurity AbstractInterceptUrlConfigurer AbstractAuthenticationProcessingFilter 类图 在前面的文章《spring-security原理与应用系列&#xff1a;securityFilterChainBuilders》中&#xff0c;我们遗留了一个问题&…

JVM生产环境问题定位与解决实战(六):总结篇——问题定位思路与工具选择策略

本文已收录于《JVM生产环境问题定位与解决实战》专栏&#xff0c;完整系列见文末目录 引言 在前五篇文章中&#xff0c;我们深入探讨了JVM生产环境问题定位与解决的实战技巧&#xff0c;从基础的jps、jmap、jstat、jstack、jcmd等工具&#xff0c;到JConsole、VisualVM、MAT的…