Java 中 Future 和 Promise 的差異
一、簡介
Future
和 Promise 是用於處理非同步任務的工具,讓人們執行操作而無需等待每一步完成。儘管它們都有相同的目的,但它們表現出關鍵的差異。在本教學中,我們將探討Future
和 Promise 之間的差異,
並仔細研究它們的關鍵特徵、用例和獨特功能。
2. 理解Future
Future
充當容器,等待正在進行的操作的結果。開發人員通常會使用Future
來檢查計算的狀態、在準備就緒時擷取結果或優雅地等待操作結束。 Future
通常與Executor
框架集成,提供一種簡單有效的方法來處理非同步任務。
2.1.主要特點
現在,讓我們來探討一下Future
的一些關鍵特徵:
- 採用阻塞設計,這可能會導致等待非同步計算完成。
- 與正在進行的計算的直接交互作用受到限制,保持了一種簡單的方法。
2.2.用例
Future
擅長處理非同步操作的結果是預先決定的並且一旦流程開始就無法更改的場景。
考慮從資料庫中取得使用者的設定檔資訊或從遠端伺服器下載檔案。一旦啟動,這些操作就會產生固定的結果,例如檢索的資料或下載的文件,並且不能在過程中修改。
2.3.使用Future
要利用Future
,我們可以在java.util.concurrent
套件中找到它們。讓我們來看一個程式碼片段,示範如何使用Future
進行非同步任務處理:
ExecutorService executorService = Executors.newSingleThreadExecutor();
Future<String> futureResult = executorService.submit(() -> {
Thread.sleep(2000);
return "Future Result";
});
while (!futureResult.isDone()) {
System.out.println("Future task is still in progress...");
Thread.sleep(500);
}
String resultFromFuture = futureResult.get();
System.out.println("Future Result: " + resultFromFuture);
executorService.shutdown();
讓我們檢查一下運行程式碼時得到的輸出:
Future task is still in progress...
Future task is still in progress...
Future task is still in progress...
Future task is still in progress...
Future Result: Future Result
在程式碼中, futureResult.get()
方法是一個阻塞呼叫。這意味著當程式到達這一行時,它將等待提交給ExecutorService
非同步任務完成後再繼續。
3. 理解 Promise
相較之下,Promise 的概念並不是 Java 原生的,而是其他程式語言中的通用抽象。 Promise 充當創建 Promise 時可能未知的值的代理。與Future
不同,Promise 通常提供更具互動性的方法,讓開發人員即使在非同步計算啟動後也可以影響非同步計算。
3.1.主要特點
現在,讓我們來探討一下 Promise 的一些關鍵特性:
- 封裝可變狀態,即使在非同步操作開始後也允許修改,從而提供處理動態場景的靈活性
- 採用回調機制,允許開發人員附加在非同步操作完成、失敗或進度時執行的回調
3.2.用例
Promise 非常適合需要對非同步操作進行動態和互動式控制的場景。此外,即使在啟動之後,Promise 也可以靈活地修改正在進行的計算。一個很好的例子是金融應用程式中的即時數據流,其中顯示內容需要適應即時市場變化。
此外,在處理需要基於中間結果進行條件分支或修改的非同步任務時,Promise 非常有用。一個可能的用例是當我們需要處理多個非同步 API 呼叫時,其中後續操作取決於先前操作的結果。
3.3.使用承諾
Java 可能沒有像 JavaScript 那樣嚴格遵守 Promise 規範的專用 Promise 類別。但是,我們可以使用java.util.concurrent.CompletableFuture
實現類似的功能。 CompletableFuture
提供了一種處理非同步任務的通用方法,與 Promise 分享一些特徵。重要的是要注意它們並不相同。
讓我們來探索如何使用CompletableFuture
在 Java 中實作類似 Promise 的行為:
ExecutorService executorService = Executors.newSingleThreadExecutor();
CompletableFuture<String> completableFutureResult = CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "CompletableFuture Result";
}, executorService);
completableFutureResult.thenAccept(result -> {
System.out.println("Promise Result: " + result);
})
.exceptionally(throwable -> {
System.err.println("Error occurred: " + throwable.getMessage());
return null;
});
System.out.println("Doing other tasks...");
executorService.shutdown();
當我們運行程式碼時,我們將看到輸出:
Doing other tasks...
Promise Result: CompletableFuture Result
我們建立一個名為completableFutureResult
的CompletableFuture
。 supplyAsync()
方法用於啟動非同步計算。提供的 lambda 函數代表非同步任務。
接下來,我們使用thenAccept()
和exceptionally()
將回呼附加到CompletableFuture
。 thenAccept()
回呼處理非同步任務的成功完成,類似於 Promise 的解析,而exceptionally()
處理任務期間可能發生的任何異常,類似於 Promise 的拒絕。
4. 主要差異
4.1.控制流
一旦設定了Future
的值,控制流程就會向下游繼續,不受後續事件或變更的影響。同時,Promise(或CompletableFuture
)提供了thenCompose()
和whenComplete()
等方法,用於根據最終結果或異常進行條件執行。
讓我們使用CompletableFuture
建立一個分支控制流的範例:
CompletableFuture<Integer> firstTask = CompletableFuture.supplyAsync(() -> {
return 1;
})
.thenApplyAsync(result -> {
return result * 2;
})
.whenComplete((result, ex) -> {
if (ex != null) {
// handle error here
}
});
在程式碼中,我們使用thenApplyAsync()
方法來示範非同步任務的連結。
4.2.錯誤處理
Future
和 Promise 都提供了處理錯誤和異常的機制。 Future
依賴計算期間拋出的異常:
ExecutorService executorService = Executors.newSingleThreadExecutor();
Future<String> futureWithError = executorService.submit(() -> {
throw new RuntimeException("An error occurred");
});
try {
String result = futureWithError.get();
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
} finally {
executorService.shutdown();
}
在CompletableFuture,
exceptionally()
方法用於處理非同步計算過程中發生的任何異常。如果發生異常,它會列印錯誤訊息並提供一個後備值:
CompletableFuture<String> promiseWithError = new CompletableFuture<>();
promiseWithError.completeExceptionally(new RuntimeException("An error occurred"));
promiseWithError.exceptionally(throwable -> {
return "Fallback value";
});
4.3.讀寫訪問
Future
提供了一個唯讀視圖,讓我們在計算完成後檢索結果:
Future<Integer> future = executor.submit(() -> 100);
// Cannot modify future.get() after completion
相較之下, CompletableFuture
不僅使我們能夠讀取結果,而且甚至在非同步操作開始後也能夠主動動態設定值:
ExecutorService executorService = Executors.newSingleThreadExecutor();
CompletableFuture<Integer> totalPromise = CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return 100;
}, executorService);
totalPromise.thenAccept(value -> System.out.println("Total $" + value ));
totalPromise.complete(10);
最初,我們將非同步任務設定為傳回 100 作為結果。然而,我們在任務自然完成之前進行幹預並明確地完成值為 10 的任務。這種靈活性凸顯了CompletableFuture
的可寫特性,讓我們在非同步執行期間動態更新結果。
5. 結論
在本文中,我們探討了Future
和 Promise 之間的差異.
雖然兩者都用於處理非同步任務,但它們的功能存在顯著差異。
與往常一樣,範例的原始程式碼可在 GitHub 上取得。