### 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>>` 的情境都沒問題!需要嗎?