Java中如何實現異步編程 掌握CompletableFuture的使用方法

Java中實現異步編程的核心在于避免線程阻塞,提高響應速度和吞吐量,主要通過completablefuture實現。1. completablefuture提供supplyasync()和runasync()創建異步任務,前者用于有返回值的任務,后者用于無返回值的任務;2. 異常處理可通過exceptionally()返回默認值或handle()統一處理結果與異常;3. 組合多個異步任務可使用thenapply()轉換結果、thencompose()鏈式依賴任務、thencombine()合并兩個獨立任務的結果;4. 線程池選擇上,默認使用forkjoinpool.commonpool()適合cpu密集型任務,i/o密集型或需控制并發時應自定義線程池并通過參數傳入;5. 避免阻塞應避免調用get()方法,改用thenaccept()或thenapplyasync()將后續操作放入異步線程執行,確保非阻塞特性。

Java中如何實現異步編程 掌握CompletableFuture的使用方法

Java中實現異步編程,核心在于避免主線程阻塞,提高程序響應速度和吞吐量。CompletableFuture是Java 8引入的強大的異步編程工具,它提供了靈活的方式來處理異步任務的結果,組合多個異步操作,以及處理異常。

Java中如何實現異步編程 掌握CompletableFuture的使用方法

CompletableFuture的使用方法

Java中如何實現異步編程 掌握CompletableFuture的使用方法

CompletableFuture提供了多種靜態方法來創建異步任務,例如supplyAsync()、runAsync()。supplyAsync()用于執行有返回值的任務,而runAsync()用于執行沒有返回值的任務。

立即學習Java免費學習筆記(深入)”;

Java中如何實現異步編程 掌握CompletableFuture的使用方法

// 執行一個有返回值的異步任務 CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {     // 模擬耗時操作     try {         Thread.sleep(1000);     } catch (InterruptedException e) {         Thread.currentThread().interrupt();         throw new RuntimeException(e);     }     return "Hello, Async!"; });  // 執行一個沒有返回值的異步任務 CompletableFuture<Void> futureVoid = CompletableFuture.runAsync(() -> {     // 模擬耗時操作     try {         Thread.sleep(500);     } catch (InterruptedException e) {         Thread.currentThread().interrupt();         throw new RuntimeException(e);     }     System.out.println("Async task completed!"); });  // 獲取異步任務的結果 (會阻塞直到結果可用) try {     String result = future.get();     System.out.println(result); } catch (Exception e) {     e.printStackTrace(); }

關鍵在于,supplyAsync和runAsync默認使用ForkJoinPool.commonPool()線程池。如果需要自定義線程池,可以傳入Executor參數。 這點需要注意,避免所有異步任務都擠在同一個公共線程池里。

如何處理異步任務的異常?

CompletableFuture提供了exceptionally()、handle()等方法來處理異常。exceptionally()允許你提供一個函數,在發生異常時返回一個默認值。handle()則允許你同時處理正常結果和異常情況。

CompletableFuture<String> futureWithException = CompletableFuture.supplyAsync(() -> {     // 模擬可能拋出異常的操作     if (Math.random() > 0.5) {         throw new RuntimeException("Something went wrong!");     }     return "Success!"; }).exceptionally(ex -> {     System.err.println("Exception occurred: " + ex.getMessage());     return "Default Value"; // 返回默認值 });  try {     String result = futureWithException.get();     System.out.println("Result: " + result); } catch (Exception e) {     e.printStackTrace(); }

handle方法更強大,它接收一個BiFunction,可以根據結果和異常進行更復雜的處理。 個人覺得,使用handle可以避免多層嵌套的try-catch,代碼更簡潔。

CompletableFuture如何組合多個異步任務?

CompletableFuture提供了thenApply()、thenCompose()、thenCombine()等方法來組合多個異步任務。thenApply()用于對異步任務的結果進行轉換。thenCompose()用于將一個異步任務的結果傳遞給另一個異步任務。thenCombine()用于合并兩個獨立的異步任務的結果。

CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> "Hello"); CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> "World");  // 使用 thenCombine 合并兩個 Future 的結果 CompletableFuture<String> combinedFuture = future1.thenCombine(future2, (s1, s2) -> s1 + ", " + s2);  // 使用 thenApply 對結果進行轉換 CompletableFuture<String> transformedFuture = combinedFuture.thenApply(String::toUpperCase);  try {     String result = transformedFuture.get();     System.out.println(result); // 輸出: HELLO, WORLD } catch (Exception e) {     e.printStackTrace(); }

thenCompose適用于一個任務的完成依賴于另一個任務的結果的場景。 例如,從數據庫查詢用戶信息,然后根據用戶信息查詢訂單信息。

CompletableFuture中的線程池選擇有什么講究?

選擇合適的線程池至關重要。 默認的ForkJoinPool.commonPool()適用于CPU密集型任務,但如果你的任務是I/O密集型,或者需要控制并發度,那么自定義線程池是更好的選擇。

// 創建一個固定大小的線程池 ExecutorService executor = Executors.newFixedThreadPool(10);  CompletableFuture<String> futureWithExecutor = CompletableFuture.supplyAsync(() -> {     // 模擬耗時操作     try {         Thread.sleep(1000);     } catch (InterruptedException e) {         Thread.currentThread().interrupt();         throw new RuntimeException(e);     }     return "Hello from custom executor!"; }, executor);  try {     String result = futureWithExecutor.get();     System.out.println(result); } catch (Exception e) {     e.printStackTrace(); } finally {     executor.shutdown(); // 關閉線程池 }

需要注意的是,使用自定義線程池后,一定要記得關閉線程池,釋放資源。 否則,可能會導致程序無法正常退出。

如何避免CompletableFuture的阻塞?

雖然get()方法可以獲取異步任務的結果,但它會阻塞當前線程。 為了避免阻塞,可以使用thenAccept()、thenApplyAsync()等方法。 thenAccept()在異步任務完成后執行一個Consumer,而thenApplyAsync()則在另一個異步任務中執行轉換操作。

CompletableFuture<String> futureNonBlocking = CompletableFuture.supplyAsync(() -> {     // 模擬耗時操作     try {         Thread.sleep(1000);     } catch (InterruptedException e) {         Thread.currentThread().interrupt();         throw new RuntimeException(e);     }     return "Non-blocking result!"; });  // 使用 thenAccept 在異步任務完成后執行一個 Consumer futureNonBlocking.thenAccept(result -> {     System.out.println("Result (non-blocking): " + result); });  // 使用 thenApplyAsync 在另一個異步任務中執行轉換操作 CompletableFuture<String> transformedFutureNonBlocking = futureNonBlocking.thenApplyAsync(String::toUpperCase);  transformedFutureNonBlocking.thenAccept(result -> {     System.out.println("Transformed result (non-blocking): " + result); });  // 注意:這里不要調用 get(),否則就阻塞了

避免阻塞的關鍵在于,不要使用get()方法,而是使用thenAccept、thenApplyAsync等方法,將后續操作也放到異步線程中執行。 這樣才能真正實現非阻塞的異步編程。

? 版權聲明
THE END
喜歡就支持一下吧
點贊15 分享