自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

Java 異步編程的幾種方式

開發(fā) 后端
異步編程是讓程序并發(fā)運行的一種手段。它允許多個事情 同時發(fā)生 ,當程序調(diào)用需要長時間運行的方法時,它不會阻塞當前的執(zhí)行流程,程序可以繼續(xù)運行,當方法執(zhí)行完成時通知給主線程根據(jù)需要獲取其執(zhí)行結(jié)果或者失敗異常的原因。

[[414800]]

異步編程是讓程序并發(fā)運行的一種手段。它允許多個事情 同時發(fā)生 ,當程序調(diào)用需要長時間運行的方法時,它不會阻塞當前的執(zhí)行流程,程序可以繼續(xù)運行,當方法執(zhí)行完成時通知給主線程根據(jù)需要獲取其執(zhí)行結(jié)果或者失敗異常的原因。使用異步編程可以大大提高我們程序的吞吐量,可以更好的面對更高的并發(fā)場景并更好的利用現(xiàn)有的系統(tǒng)資源,同時也會一定程度上減少用戶的等待時間等。本文我們一起來看看在 Java 語言中使用異步編程有哪些方式。

Thread 方式

在 Java 語言中最簡單使用異步編程的方式就是創(chuàng)建一個 Thread 來實現(xiàn),如果你使用的 JDK 版本是 8 以上的話,可以使用 Lambda 表達式 會更加簡潔。為了能更好的體現(xiàn)出異步的高效性,下面提供同步版本和異步版本的示例作為對照:

  1. /** 
  2.  * @author mghio 
  3.  * @since 2021-08-01 
  4.  */ 
  5. public class SyncWithAsyncDemo { 
  6.  
  7.   public static void doOneThing() { 
  8.     try { 
  9.       Thread.sleep(2000); 
  10.     } catch (InterruptedException e) { 
  11.       e.printStackTrace(); 
  12.     } 
  13.     System.out.println("doOneThing ---->>> success"); 
  14.   } 
  15.  
  16.   public static void doOtherThing() { 
  17.     try { 
  18.       Thread.sleep(2000); 
  19.     } catch (InterruptedException e) { 
  20.       e.printStackTrace(); 
  21.     } 
  22.     System.out.println("doOtherThing ---->>> success"); 
  23.   } 
  24.  
  25.   public synchronized static void main(String[] args) throws InterruptedException { 
  26.     StopWatch stopWatch = new StopWatch("SyncWithAsyncDemo"); 
  27.     stopWatch.start(); 
  28.  
  29.     // 同步調(diào)用版本 
  30.     // testSynchronize(); 
  31.  
  32.     // 異步調(diào)用版本 
  33.     testAsynchronize(); 
  34.  
  35.     stopWatch.stop(); 
  36.     System.out.println(stopWatch); 
  37.   } 
  38.  
  39.   private static void testAsynchronize() throws InterruptedException { 
  40.     System.out.println("-------------------- testAsynchronize --------------------"); 
  41.  
  42.     // 創(chuàng)建一個線程執(zhí)行 doOneThing 
  43.     Thread doOneThingThread = new Thread(SyncWithAsyncDemo::doOneThing, "doOneThing-Thread"); 
  44.     doOneThingThread.start(); 
  45.  
  46.     doOtherThing(); 
  47.     // 等待 doOneThing 線程執(zhí)行完成 
  48.     doOneThingThread.join(); 
  49.   } 
  50.  
  51.   private static void testSynchronize() { 
  52.     System.out.println("-------------------- testSynchronize --------------------"); 
  53.  
  54.     doOneThing(); 
  55.     doOtherThing(); 
  56.   } 
  57.  

同步執(zhí)行的運行如下:

注釋掉同步調(diào)用版本的代碼,得到異步執(zhí)行的結(jié)果如下:

從兩次的運行結(jié)果可以看出,同步版本耗時 4002 ms ,異步版本執(zhí)行耗時 2064 ms ,異步執(zhí)行耗時減少將近一半,可以看出使用異步編程后可以大大縮短程序運行時間。

上面的示例的異步線程代碼在 main 方法內(nèi)開啟了一個線程 doOneThing-Thread 用來異步執(zhí)行 doOneThing 任務(wù),在這時該線程與 main 主線程并發(fā)運行,也就是任務(wù) doOneThing 與任務(wù) doOtherThing 并發(fā)運行,則等主線程運行完 doOtherThing 任務(wù)后同步等待線程 doOneThing 運行完畢,整體還是比較簡單的。

但是這個示例只能作為示例使用,如果用到了生產(chǎn)環(huán)境發(fā)生事故后果自負,使用上面這種 Thread 方式異步編程存在兩個明顯的問題。

  1. FutureTask 

FutureTask 方式

自 JDK 1.5 開始,引入了 Future 接口和實現(xiàn) Future 接口的 FutureTask 類來表示異步計算結(jié)果。這個 FutureTask 類不僅實現(xiàn)了 Future 接口還實現(xiàn)了 Runnable 接口,表示一種可生成結(jié)果的 Runnable 。其可以處于這三種狀態(tài):

  • 未啟動 當創(chuàng)建一個 FutureTask 沒有執(zhí)行 FutureTask.run() 方法之前
  • 已啟動 在 FutureTask.run() 方法執(zhí)行的過程中
  • 已完成 在 FutureTask.run() 方法正常執(zhí)行結(jié)果或者調(diào)用了 FutureTask.cancel(boolean mayInterruptIfRunning) 方法以及在調(diào)用 FutureTask.run() 方法的過程中發(fā)生異常結(jié)束后

FutureTask 類實現(xiàn)了 Future 接口的開啟和取消任務(wù)、查詢?nèi)蝿?wù)是否完成、獲取計算結(jié)果方法。要獲取 FutureTask 任務(wù)的結(jié)果,我們只能通過調(diào)用 getXXX() 系列方法才能獲取,當結(jié)果還沒出來時候這些方法會被阻塞,同時這了任務(wù)可以是 Callable 類型(有返回結(jié)果),也可以是 Runnable 類型(無返回結(jié)果)。我們修改上面的示例把兩個任務(wù)方法修改為返回 String 類型,使用 FutureTask 的方法如下:

  1. private static void testFutureTask() throws ExecutionException, InterruptedException { 
  2.     System.out.println("-------------------- testFutureTask --------------------"); 
  3.  
  4.     // 創(chuàng)建一個 FutureTask(doOneThing 任務(wù)) 
  5.     FutureTask<String> futureTask = new FutureTask<>(FutureTaskDemo::doOneThing); 
  6.     // 使用線程池執(zhí)行 doOneThing 任務(wù) 
  7.     ForkJoinPool.commonPool().execute(futureTask); 
  8.  
  9.     // 執(zhí)行 doOtherThing 任務(wù) 
  10.     String doOtherThingResult = doOtherThing(); 
  11.  
  12.     // 同步等待線程執(zhí)行 doOneThing 任務(wù)結(jié)束 
  13.     String doOneThingResult = futureTask.get(); 
  14.  
  15.     // 任務(wù)執(zhí)行結(jié)果輸出 
  16.     System.out.println("doOneThingResult ---->>> " + doOneThingResult); 
  17.     System.out.println("doOtherThingResult ---->>> " + doOtherThingResult); 

使用 FutureTask 異步編程方式的耗時和上面的 Thread 方式是差不多的,其本質(zhì)都是另起一個線程去做 doOneThing 任務(wù)然后等待返回,運行結(jié)果如下:

這個示例中, doOneThing 和 doOtherThing 都是有返回值的任務(wù)(都返回 String 類型結(jié)果),我們在主線程 main 中創(chuàng)建一個異步任務(wù) FutureTask 來執(zhí)行 doOneThing ,然后使用 ForkJoinPool.commonPool() 創(chuàng)建線程池(有關(guān) ForkJoinPool 的介紹見這里),然后調(diào)用了線程池的 execute 方法把 futureTask 提交到線程池來執(zhí)行。

通過示例可以看到,雖然 FutureTask 提供了一些方法讓我們獲取任務(wù)的執(zhí)行結(jié)果、任務(wù)是否完成等,但是使用還是比較復(fù)雜,在一些較為復(fù)雜的場景(比如多個 FutureTask 之間的關(guān)系表示)的編碼還是比較繁瑣,還是當我們調(diào)用 getXXX() 系列方法時還是會在任務(wù)執(zhí)行完畢前阻塞調(diào)用線程,達不到異步編程的效果,基于這些問題,在 JDK 8 中引入了 CompletableFuture 類,下面來看看如何使用 CompletableFuture 來實現(xiàn)異步編程。

CompletableFuture 方式

JDK 8 中引入了 CompletableFuture 類,實現(xiàn)了 Future 和 CompletionStage 接口,為異步編程提供了一些列方法,如 supplyAsync 、 runAsync 和 thenApplyAsync 等,除此之外 CompletableFuture 還有一個重要的功能就是可以讓兩個或者多個 CompletableFuture 進行運算來產(chǎn)生結(jié)果。代碼如下:

  1. /** 
  2.  * @author mghio 
  3.  * @since 2021-08-01 
  4.  */ 
  5. public class CompletableFutureDemo { 
  6.  
  7.   public static CompletableFuture<String> doOneThing() { 
  8.     return CompletableFuture.supplyAsync(() -> { 
  9.       try { 
  10.         Thread.sleep(2000); 
  11.       } catch (InterruptedException e) { 
  12.         e.printStackTrace(); 
  13.       } 
  14.       return "doOneThing"
  15.     }); 
  16.   } 
  17.  
  18.   public static CompletableFuture<String> doOtherThing(String parameter) { 
  19.     return CompletableFuture.supplyAsync(() -> { 
  20.       try { 
  21.         Thread.sleep(2000); 
  22.       } catch (InterruptedException e) { 
  23.         e.printStackTrace(); 
  24.       } 
  25.       return parameter + " " + "doOtherThing"
  26.     }); 
  27.   } 
  28.  
  29.   public static void main(String[] args) throws ExecutionException, InterruptedException { 
  30.     StopWatch stopWatch = new StopWatch("CompletableFutureDemo"); 
  31.     stopWatch.start(); 
  32.  
  33.     // 異步執(zhí)行版本 
  34.     testCompletableFuture(); 
  35.  
  36.     stopWatch.stop(); 
  37.     System.out.println(stopWatch); 
  38.   } 
  39.  
  40.   private static void testCompletableFuture() throws InterruptedException, ExecutionException { 
  41.     // 先執(zhí)行 doOneThing 任務(wù),后執(zhí)行 doOtherThing 任務(wù) 
  42.     CompletableFuture<String> resultFuture = doOneThing().thenCompose(CompletableFutureDemo::doOtherThing); 
  43.  
  44.     // 獲取任務(wù)結(jié)果 
  45.     String doOneThingResult = resultFuture.get(); 
  46.  
  47.     // 獲取執(zhí)行結(jié)果 
  48.     System.out.println("DoOneThing and DoOtherThing execute finished. result = " + doOneThingResult); 
  49.   } 
  50.  

執(zhí)行結(jié)果如下:

在主線程 main 中首先調(diào)用了方法 doOneThing() 方法開啟了一個異步任務(wù),并返回了對應(yīng)的 CompletableFuture 對象,我們?nèi)∶麨?nbsp;doOneThingFuture ,然后在 doOneThingFuture 的基礎(chǔ)上使用 CompletableFuture 的 thenCompose() 方法,讓 doOneThingFuture 方法執(zhí)行完成后,使用其執(zhí)行結(jié)果作為 doOtherThing(String parameter) 方法的參數(shù)創(chuàng)建的異步任務(wù)返回。

我們不需要顯式使用 ExecutorService ,在 CompletableFuture 內(nèi)部使用的是 Fork/Join 框架異步處理任務(wù),因此,它使我們編寫的異步代碼更加簡潔。此外, CompletableFuture 類功能很強大其提供了和很多方便的方法,更多關(guān)于 CompletableFuture 的使用請見這篇。

 

責(zé)任編輯:張燕妮 來源: mghio's blog
相關(guān)推薦

2021-02-27 16:08:17

Java異步非阻塞

2021-05-07 16:19:36

異步編程Java線程

2024-04-24 10:57:54

Golang編程

2019-05-16 13:00:18

異步編程JavaScript回調(diào)函數(shù)

2024-10-15 08:29:09

C#軟件開發(fā)

2022-07-01 08:00:44

異步編程FutureTask

2013-04-01 15:38:54

異步編程異步編程模型

2011-02-22 09:09:21

.NETAsync CTP異步

2011-02-22 08:49:16

.NET同步異步

2013-04-01 15:25:41

異步編程異步EMP

2020-09-23 07:47:14

Java方式類型

2010-09-25 14:48:55

SQL連接

2021-01-19 11:56:19

Python開發(fā)語言

2024-04-18 08:20:27

Java 8編程工具

2021-03-22 08:45:30

異步編程Java

2020-10-15 13:29:57

javascript

2023-08-07 16:14:32

propertiesSpring框架

2010-11-24 09:56:20

mysql拷貝表

2021-10-07 20:36:45

Redis集群場景

2023-09-07 13:21:00

Linux軟件
點贊
收藏

51CTO技術(shù)棧公眾號