markdown-it
demo
Delete
Submit
clear
permalink
### CompletableFuture ```java import java.util.concurrent.CompletableFuture; public class CompletableFutureExample { public static void main(String[] args) { // 創建一個CompletableFuture並在另一個執行緒中執行一個供應器,返回字串"Hello" CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> { return "Hello"; }).thenApplyAsync(result -> { // 使用thenApplyAsync方法對"Hello"進行處理,返回"Hello World" return result + " World"; }); // 當CompletableFuture完成後執行操作,印出結果"Hello World" future.thenAccept(result -> { System.out.println(result); // Hello World }); // 等待CompletableFuture完成 future.join(); } } ``` --- 在 **Java 8** 中,`CompletableFuture` **沒有內建的 `completeOnTimeout()` 或 `orTimeout()`**,所以你需要透過「手動建立 timeout 任務」的方式來實現 **5 秒內完成返回結果,否則給預設值**。 --- ## ✅ Java 8 實作:使用 `applyToEither` 搭配 timeout future ### 🔧 目標: * 等 `realTask` 執行結果 * 若超過 5 秒沒回來,回傳 `"Default Value"` --- ### ✅ 範例程式碼 ```java import java.util.concurrent.*; public class CompletableFutureTimeoutExample { public static void main(String[] args) { ExecutorService executor = Executors.newCachedThreadPool(); // 模擬實際任務,10 秒才完成 CompletableFuture<String> realTask = CompletableFuture.supplyAsync(() -> { try { Thread.sleep(10000); // 假設這個任務執行很久 } catch (InterruptedException e) { e.printStackTrace(); } return "Real Result"; }, executor); // 建立 timeout 任務,5 秒後回傳預設值 CompletableFuture<String> timeout = CompletableFuture.supplyAsync(() -> { try { Thread.sleep(5000); // timeout 等待時間 } catch (InterruptedException e) { e.printStackTrace(); } return "Default Value"; }, executor); // 誰先完成就用誰的結果 CompletableFuture<String> result = realTask.applyToEither(timeout, value -> value); // 等待完成並印出結果 System.out.println(result.join()); executor.shutdown(); } } ``` --- ### ✅ 執行結果(因為 realTask 花 10 秒,timeout 5 秒先完成): ``` Default Value ``` --- ## 🧠 延伸建議 * 若你有很多類似場景,建議包成工具函式。 * 可進一步封裝成如下: ```java public static <T> CompletableFuture<T> withTimeout( CompletableFuture<T> task, T defaultValue, long timeout, TimeUnit unit, Executor executor ) { CompletableFuture<T> timeoutFuture = CompletableFuture.supplyAsync(() -> { try { unit.sleep(timeout); } catch (InterruptedException ignored) {} return defaultValue; }, executor); return task.applyToEither(timeoutFuture, Function.identity()); } ``` --- 以下是一個適用於 **Java 8** 的 `CompletableFuture` 工具類,用來實現: > ✅ **執行非同步任務**, > ⏱️ **在 timeout 時間內沒完成,就回傳預設值**, > 💡 可傳入自訂 `ExecutorService`(執行緒池)。 --- ## ✅ `FutureUtils.java` 工具類 ```java import java.util.concurrent.*; import java.util.function.Function; import java.util.function.Supplier; public class FutureUtils { /** * 執行一個非同步任務,若在 timeout 時間內未完成,回傳 defaultValue。 * * @param task 任務的 supplier * @param defaultValue 逾時時的預設值 * @param timeout timeout 時間 * @param unit 時間單位 * @param executor 執行緒池 * @param <T> 回傳型別 * @return CompletableFuture<T> */ public static <T> CompletableFuture<T> runWithTimeout( Supplier<T> task, T defaultValue, long timeout, TimeUnit unit, Executor executor ) { CompletableFuture<T> realTask = CompletableFuture.supplyAsync(task, executor); CompletableFuture<T> timeoutTask = CompletableFuture.supplyAsync(() -> { try { unit.sleep(timeout); } catch (InterruptedException ignored) { } return defaultValue; }, executor); return realTask.applyToEither(timeoutTask, Function.identity()); } } ``` --- ## ✅ 使用範例 ```java import java.util.concurrent.*; public class Main { public static void main(String[] args) { ExecutorService executor = Executors.newFixedThreadPool(5); CompletableFuture<String> future = FutureUtils.runWithTimeout(() -> { try { Thread.sleep(6000); // 假設這任務花了 6 秒 } catch (InterruptedException e) { e.printStackTrace(); } return "Actual Result"; }, "Default Value", 5, TimeUnit.SECONDS, executor); System.out.println("Result: " + future.join()); // 輸出:Default Value executor.shutdown(); } } ``` --- ## 🧠 補充建議 * 若未來想擴充 `onTimeout()` 回呼,可以改用 `whenComplete()`。 * 若要取消超時後仍在跑的任務,可以額外追蹤 `realTask` 並呼叫 `.cancel(true)`(需處理中斷)。 --- 如果你有使用 Spring,這個也可以包裝成 Spring Bean + 使用 `@Async` 的風格。我可以幫你加上,或改為 `CompletableFuture<List<T>>` 的情境都沒問題!需要嗎?
html
source
debug
Fork me on GitHub