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.pswp.cn/pingmian/75350.shtml
繁體地址,請注明出處:http://hk.pswp.cn/pingmian/75350.shtml
英文地址,請注明出處:http://en.pswp.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;識別出圖像中不同的區域和對象。這一步是為了讓模型理解圖像的內…