Java中5種異步實(shí)現(xiàn)的方式詳解
一、什么是異步
首先我們先來看看一個同步的用戶注冊例子,流程如下:
在同步操作中,我們執(zhí)行到插入數(shù)據(jù)庫的時候,我們必須等待這個方法徹底執(zhí)行完才能執(zhí)行“發(fā)送短信”這個操作,如果插入數(shù)據(jù)庫這個動作執(zhí)行時間較長,發(fā)送短信需要等待,這就是典型的同步場景。
于是聰明的人們開始思考,如果兩者關(guān)聯(lián)性不強(qiáng),能不能將一些非核心業(yè)務(wù)從主流程中剝離出來,于是有了異步編程雛形,改進(jìn)后的流程如下:
這就是異步編程,它是程序并發(fā)運(yùn)行的一種手段,它允許多個事件同時發(fā)生,當(dāng)程序調(diào)用需要長時間運(yùn)行的方法時,它不會阻塞當(dāng)前的執(zhí)行流程,程序可以繼續(xù)運(yùn)行。
在聊完異步編程后,那么我們一起來看看Java里面實(shí)現(xiàn)異步編程究竟有哪些方式呢?
二、線程異步
在 Java 語言中最簡單使用異步編程的方式就是創(chuàng)建一個 線程來實(shí)現(xiàn),如果你使用的 JDK 版本是 8 以上的話,可以使用 Lambda 表達(dá)式 會更加簡潔。
public class AsyncThread extends Thread{ @Override public void run() { System.out.println("當(dāng)前線程名稱:" + this.getName() + ", 執(zhí)行線程名稱:" + Thread.currentThread().getName() + "-hello"); } }
public static void main(String[] args) { // 模擬業(yè)務(wù)流程 // ....... // 創(chuàng)建異步線程 AsyncThread asyncThread = new AsyncThread(); // 啟動異步線程 asyncThread.start(); }
當(dāng)然如果每次都創(chuàng)建一個 Thread線程,頻繁的創(chuàng)建、銷毀,浪費(fèi)系統(tǒng)資源,我們可以采用線程池:
private ExecutorService executor = Executors.newCachedThreadPool() ; public void fun() throws Exception { executor.submit(new Runnable(){ @override public void run() { try { //要執(zhí)行的業(yè)務(wù)代碼,我們這里沒有寫方法,可以讓線程休息幾秒進(jìn)行測試 Thread.sleep(10000); System.out.print("睡夠啦~"); }catch(Exception e) { throw new RuntimeException("報錯啦??!"); } } }); }
將業(yè)務(wù)邏輯封裝到 Runnable 或 Callable 中,交由 線程池 來執(zhí)行。
三、Future異步
上述方式雖然達(dá)到了多線程并行處理,但有些業(yè)務(wù)不僅僅要執(zhí)行過程,還要獲取執(zhí)行結(jié)果,后續(xù)提供在JUC包增加了Future。
從字面意思理解就是未來的意思,但使用起來卻著實(shí)有點(diǎn)雞肋,并不能實(shí)現(xiàn)真正意義上的異步,獲取結(jié)果時需要阻塞線程,或者不斷輪詢。
@Test public void futureTest() throws Exception { System.out.println("main函數(shù)開始執(zhí)行"); ExecutorService executor = Executors.newFixedThreadPool(1); Future<Integer> future = executor.submit(new Callable<Integer>() { @Override public Integer call() throws Exception { System.out.println("===task start==="); Thread.sleep(5000); System.out.println("===task finish==="); return 3; } }); //這里需要返回值時會阻塞主線程,如果不需要返回值使用是OK的。倒也還能接收 //Integer result=future.get(); System.out.println("main函數(shù)執(zhí)行結(jié)束"); System.in.read(); }
四、CompletableFuture異步
Future 類通過 get() 方法阻塞等待獲取異步執(zhí)行的運(yùn)行結(jié)果,性能比較差。
JDK1.8 中,Java 提供了 CompletableFuture 類,它是基于異步函數(shù)式編程。相對阻塞式等待返回結(jié)果,CompletableFuture 可以通過回調(diào)的方式來處理計算結(jié)果,實(shí)現(xiàn)了異步非阻塞,性能更優(yōu)。
CompletableFuture 實(shí)現(xiàn)了 Future 和 CompletionStage 接口, 并提供了多種實(shí)現(xiàn)異步編程的方法,如supplyAsync, runAsync以及thenApplyAsync。
下面我們使用CompletableFuture來實(shí)現(xiàn)上面的例子:
CompletableFuture<Long> completableFuture = CompletableFuture.supplyAsync(() -> factorial(number)); while (!completableFuture.isDone()) { System.out.println("CompletableFuture is not finished yet..."); } long result = completableFuture.get();
我們不需要顯式使用 ExecutorService,CompletableFuture 內(nèi)部使用了 ForkJoinPool 來處理異步任務(wù),這使得我們的代碼變的更簡潔。
五、SpringBoot @Async異步
在@Async注解之前,使用多線程需要使用JDK的原生方法,非常麻煩,當(dāng)有了@Async之后就比較簡單了。
首先,使用 @EnableAsync 啟用異步注解:
@SpringBootApplication @EnableAsync public class StartApplication { public static void main(String[] args) { SpringApplication.run(StartApplication.class, args); } }
自定義線程池:
@Configuration @Slf4j public class ThreadPoolConfiguration { @Bean(name = "defaultThreadPoolExecutor", destroyMethod = "shutdown") public ThreadPoolExecutor systemCheckPoolExecutorService() { return new ThreadPoolExecutor(3, 10, 60, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>(10000), new ThreadFactoryBuilder().setNameFormat("default-executor-%d").build(), (r, executor) -> log.error("system pool is full! ")); } }
在異步處理的方法上添加注解 @Async ,當(dāng)對 execute 方法 調(diào)用時,通過自定義的線程池 defaultThreadPoolExecutor 異步化執(zhí)行 execute 方法
@Service public class AsyncServiceImpl implements AsyncService { @Async("defaultThreadPoolExecutor") public Boolean execute(Integer num) { System.out.println("線程:" + Thread.currentThread().getName() + " , 任務(wù):" + num); return true; } }
用 @Async 注解標(biāo)記的方法,稱為異步方法。在spring boot應(yīng)用中使用 @Async 很簡單:
- 調(diào)用異步方法類上或者啟動類加上注解 @EnableAsync
- 在需要被異步調(diào)用的方法外加上 @Async
- 所使用的 @Async 注解方法的類對象應(yīng)該是Spring容器管理的bean對象;
六、Guava異步
Guava 提供了 ListenableFuture 類來執(zhí)行異步操作
1.首先我們需要添加 guava 的maven依賴:
<dependency> <groupId>com.google.guava</groupId> <artifactId>guava</artifactId> <version>28.2-jre</version> </dependency>
2.現(xiàn)在我們使用ListenableFuture來實(shí)現(xiàn)我們之前的例子:
ExecutorService threadpool = Executors.newCachedThreadPool(); ListeningExecutorService service = MoreExecutors.listeningDecorator(threadpool); ListenableFuture<Long> guavaFuture = (ListenableFuture<Long>) service.submit(()-> factorial(number)); long result = guavaFuture.get();
這里使用MoreExecutors獲取ListeningExecutorService類的實(shí)例,然后ListeningExecutorService.submit執(zhí)行異步任務(wù),并返回 ListenableFuture實(shí)例。
到此這篇關(guān)于Java中5種異步實(shí)現(xiàn)的方式詳解的文章就介紹到這了,更多相關(guān)Java異步實(shí)現(xiàn)內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java使用IntelliJ IDEA連接MySQL的詳細(xì)教程
這篇文章主要給大家介紹了關(guān)于Java使用IntelliJ IDEA連接MySQL的相關(guān)資料,文中通過圖文介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-04-04Java幸運(yùn)28系統(tǒng)搭建數(shù)組的使用實(shí)例詳解
在本篇文章里小編給大家整理了關(guān)于Java幸運(yùn)28系統(tǒng)搭建數(shù)組的使用實(shí)例內(nèi)容,有需要的朋友們可以參考學(xué)習(xí)下。2019-09-09詳解Http請求中Content-Type講解以及在Spring MVC中的應(yīng)用
這篇文章主要介紹了Http請求中Content-Type講解以及在Spring MVC中的應(yīng)用的相關(guān)資料,需要的朋友可以參考下2017-02-02關(guān)于SpringBoot使用Redis空指針的問題(不能成功注入的問題)
這篇文章主要介紹了關(guān)于SpringBoot使用Redis空指針的問題(不能成功注入的問題),本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-11-11@RequestBody,@RequestParam和@Param的區(qū)別說明
這篇文章主要介紹了@RequestBody,@RequestParam和@Param的區(qū)別說明,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-03-03詳解Spring boot Admin 使用eureka監(jiān)控服務(wù)
本篇文章主要介紹了詳解Spring boot Admin 使用eureka監(jiān)控服務(wù),小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-12-12VsCode配置java環(huán)境的詳細(xì)圖文教程
vscode是一個免費(fèi)的代碼編輯器,支持多種主題,應(yīng)用起來簡單方便,下面這篇文章主要給大家介紹了關(guān)于VsCode配置java環(huán)境的詳細(xì)圖文教程,文中通過圖文介紹的非常詳細(xì),需要的朋友可以參考下2023-02-02Java實(shí)現(xiàn)雙端鏈表LinkedList
本文主要介紹了Java實(shí)現(xiàn)雙端鏈表LinkedList,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2022-07-07Spring?Cloud?Stream消息驅(qū)動組件使用方法介紹
Spring?Cloud?Stream?消息驅(qū)動組件幫助我們更快速,更方便,更友好的去構(gòu)建消息驅(qū)動微服務(wù)的。當(dāng)時定時任務(wù)和消息驅(qū)動的?個對比。消息驅(qū)動:基于消息機(jī)制做一些事情2022-09-09