Java中future的主要作用是代表異步計算的結果,允許非阻塞地獲取任務結果并提高并發效率。1.get()方法可阻塞式獲取結果或設置超時;2.isdone()方法用于非阻塞檢查任務是否完成;3.通過第三方庫如guava的listenablefuture實現回調機制處理任務完成后自動執行的操作。此外,future.cancel()可用于嘗試取消任務,適用于資源釋放、任務超時等場景。而futuretask作為runnable和future的結合體,既能提交執行也能獲取結果,其內部狀態機管理任務生命周期。異常處理可通過get()中的try-catch捕獲executionexception或使用回調機制處理。相比completablefuture,future功能較為基礎,后者提供鏈式調用、組合任務、更靈活的異常處理等功能,更適合復雜異步編程需求。
Java中Future的主要作用是代表異步計算的結果。它允許你在提交一個任務后,不必立即等待任務完成,而是先拿到一個Future對象,稍后再通過這個對象來獲取計算結果。這在并發編程中非常有用,可以提高程序的響應速度和吞吐量。
Future提供了一種非阻塞的方式來獲取異步任務的結果,避免了主線程被長時間阻塞的情況。你可以先執行其他的任務,然后在需要結果的時候再通過Future來獲取。
解析異步計算結果的獲取方式主要有三種:get() 方法、isDone() 方法和回調機制。
立即學習“Java免費學習筆記(深入)”;
get() 方法:阻塞式獲取結果,直到任務完成或超時 Future接口最常用的方法之一就是get()。當你調用future.get()時,如果任務還沒有完成,調用線程會被阻塞,直到任務完成并返回結果,或者發生異常。get()方法還可以接受一個超時參數,如果在指定的時間內任務沒有完成,會拋出TimeoutException。
ExecutorService executor = Executors.newFixedThreadPool(10); Future<String> future = executor.submit(() -> { Thread.sleep(2000); // 模擬耗時操作 return "任務完成"; }); try { String result = future.get(3, TimeUnit.SECONDS); // 設置超時時間為3秒 System.out.println("結果: " + result); } catch (InterruptedException | ExecutionException | TimeoutException e) { e.printStackTrace(); } finally { executor.shutdown(); }
這個例子中,如果任務在3秒內沒有完成,future.get()會拋出TimeoutException。
isDone() 方法:非阻塞式檢查任務是否完成 isDone()方法允許你檢查任務是否已經完成,而不會阻塞當前線程。你可以使用這個方法來定期檢查任務的狀態,并在任務完成后獲取結果。
ExecutorService executor = Executors.newFixedThreadPool(10); Future<String> future = executor.submit(() -> { Thread.sleep(2000); // 模擬耗時操作 return "任務完成"; }); while (!future.isDone()) { System.out.println("任務還在執行中..."); try { Thread.sleep(500); // 每隔500毫秒檢查一次 } catch (InterruptedException e) { e.printStackTrace(); } } try { String result = future.get(); System.out.println("結果: " + result); } catch (InterruptedException | ExecutionException e) { e.printStackTrace(); } finally { executor.shutdown(); }
回調機制:任務完成后自動執行回調函數
雖然Future本身沒有直接提供回調機制,但可以通過一些庫(如Guava的ListenableFuture)或者自己實現類似的功能。回調機制允許你在任務完成后自動執行一段代碼,而不需要手動調用get()或者isDone()。
import com.google.common.util.concurrent.FutureCallback; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.MoreExecutors; ExecutorService executor = Executors.newFixedThreadPool(10); ListenableFuture<String> future = MoreExecutors.listeningDecorator(executor).submit(() -> { Thread.sleep(2000); // 模擬耗時操作 return "任務完成"; }); Futures.addCallback(future, new FutureCallback<String>() { @Override public void onSuccess(String result) { System.out.println("任務成功完成,結果: " + result); } @Override public void onFailure(Throwable t) { System.err.println("任務失敗: " + t.getMessage()); } }, MoreExecutors.directExecutor()); executor.shutdown();
Guava的ListenableFuture提供了一個addCallback方法,可以在任務完成后執行onSuccess或onFailure回調。
Future.cancel()方法的作用和使用場景
Future.cancel() 方法用于嘗試取消與 Future 關聯的異步任務。它接收一個 Boolean 類型的參數 mayInterruptIfRunning,決定是否允許中斷正在運行的任務。
- mayInterruptIfRunning = true: 嘗試中斷正在運行的任務。這通常通過調用任務執行線程的 interrupt() 方法來實現。然而,任務是否真正響應中斷取決于任務本身的實現。
- mayInterruptIfRunning = false: 不允許中斷正在運行的任務。如果任務尚未開始執行,則會被取消,否則任務會繼續執行直到完成。
使用場景:
- 資源釋放: 當不再需要異步任務的結果時,可以調用 cancel() 方法來釋放資源,避免不必要的計算。
- 任務超時: 如果異步任務執行時間過長,超過了預期的閾值,可以使用 cancel() 方法來終止任務,防止資源被長時間占用。
- 程序退出: 在程序退出前,可以嘗試取消所有未完成的異步任務,確保資源得到正確釋放。
需要注意的是,cancel() 方法并不保證任務一定會被取消。如果任務已經完成或已經被取消,調用 cancel() 方法不會產生任何影響。此外,即使 mayInterruptIfRunning 為 true,任務也可能忽略中斷信號,繼續執行直到完成。因此,在設計異步任務時,應該考慮如何優雅地處理中斷信號,確保任務能夠及時停止并釋放資源。
FutureTask的原理和使用
FutureTask 是一個可取消的異步計算任務,它實現了 RunnableFuture 接口,該接口繼承自 Runnable 和 Future。 這意味著 FutureTask 既可以作為一個 Runnable 提交給 ExecutorService 執行,也可以作為一個 Future 來獲取異步計算的結果。
原理:
FutureTask 內部維護了一個狀態機,用于表示任務的不同狀態:
- NEW: 任務創建后的初始狀態。
- COMPLETING: 任務正在完成,正在設置結果或拋出異常。
- NORMAL: 任務正常完成,結果已設置。
- EXCEPTIONAL: 任務執行過程中發生異常。
- CANCELLED: 任務已被取消。
- INTERRUPTING: 任務正在中斷。
- INTERRUPTED: 任務已被中斷。
FutureTask 通過 run() 方法來執行任務。在 run() 方法中,它會調用任務的 call() 方法來執行實際的計算。計算結果會被保存,并將狀態設置為 NORMAL 或 EXCEPTIONAL。
使用:
import java.util.concurrent.*; public class FutureTaskExample { public static void main(String[] args) throws InterruptedException, ExecutionException { Callable<String> callable = () -> { Thread.sleep(1000); return "任務完成!"; }; FutureTask<String> futureTask = new FutureTask<>(callable); ExecutorService executor = Executors.newSingleThreadExecutor(); executor.submit(futureTask); System.out.println("任務提交..."); try { String result = futureTask.get(); // 阻塞等待結果 System.out.println("結果: " + result); } catch (InterruptedException | ExecutionException e) { e.printStackTrace(); } finally { executor.shutdown(); } } }
這個例子展示了如何使用 FutureTask 來執行一個異步任務。首先,創建一個 Callable 對象,表示要執行的任務。然后,創建一個 FutureTask 對象,并將 Callable 對象傳遞給它。接下來,將 FutureTask 對象提交給 ExecutorService 執行。最后,通過 futureTask.get() 方法來獲取異步計算的結果。
如何處理Future中的異常
處理 Future 中的異常主要有兩種方式:
- 在 get() 方法中捕獲異常: Future.get() 方法會拋出 InterruptedException 和 ExecutionException 兩種異常。InterruptedException 表示當前線程在等待結果時被中斷。ExecutionException 表示在任務執行過程中發生了異常。可以通過 try-catch 塊來捕獲這些異常,并進行相應的處理。
ExecutorService executor = Executors.newFixedThreadPool(1); Future<String> future = executor.submit(() -> { // 模擬異常 throw new RuntimeException("任務執行出錯!"); }); try { String result = future.get(); System.out.println("結果: " + result); // 這行代碼不會被執行 } catch (InterruptedException e) { System.err.println("線程被中斷: " + e.getMessage()); } catch (ExecutionException e) { System.err.println("任務執行出錯: " + e.getCause().getMessage()); } finally { executor.shutdown(); }
在這個例子中,ExecutionException 的 getCause() 方法可以獲取到原始的異常信息。
- 使用回調機制處理異常: 如前面提到的Guava的ListenableFuture,可以在回調函數中處理異常。
import com.google.common.util.concurrent.FutureCallback; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.MoreExecutors; ExecutorService executor = Executors.newFixedThreadPool(1); ListenableFuture<String> future = MoreExecutors.listeningDecorator(executor).submit(() -> { // 模擬異常 throw new RuntimeException("任務執行出錯!"); }); Futures.addCallback(future, new FutureCallback<String>() { @Override public void onSuccess(String result) { System.out.println("任務成功完成,結果: " + result); // 這行代碼不會被執行 } @Override public void onFailure(Throwable t) { System.err.println("任務失敗: " + t.getMessage()); } }, MoreExecutors.directExecutor()); executor.shutdown();
使用回調機制可以更加靈活地處理異常,避免阻塞主線程。
Future與CompletableFuture的區別
Future 和 CompletableFuture 都是 Java 中用于處理異步計算結果的接口,但 CompletableFuture 是 Java 8 引入的,相比 Future 提供了更強大的功能和更靈活的編程模型。
- 更豐富的 API: CompletableFuture 提供了更多的 API,可以進行鏈式調用、組合多個異步任務、處理異常等。Future 的 API 相對簡單,只能獲取結果和取消任務。
- 非阻塞性: CompletableFuture 提供了非阻塞的 API,可以在任務完成時自動觸發回調函數,避免了 Future.get() 方法的阻塞等待。
- 組合性: CompletableFuture 可以將多個異步任務組合成一個新的異步任務,例如 thenApply()、thenCompose()、thenCombine() 等方法。
- 異常處理: CompletableFuture 提供了更強大的異常處理機制,可以使用 exceptionally()、handle() 等方法來處理異步任務中的異常。
- 更易于使用: CompletableFuture 的 API 設計更加友好,更容易使用和理解。
簡單來說,CompletableFuture 是 Future 的增強版,提供了更多的功能和更靈活的編程模型,更適合構建復雜的異步應用。如果只需要簡單的異步計算,可以使用 Future。如果需要更強大的功能和更靈活的編程模型,應該使用 CompletableFuture。