我喜歡Java 8的CompletableFuture,但它有它的缺點: 慣用的超時處理就是其中之一。
JAVA 8我們只能收集異常信息,再次執行什么的(以下是JAVA8解決超時的方式,獲取結果后你該做什么做什么):
//我們讓list里傳入方法的參數1號報錯和5號超時 public class Test4 { private static final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool( 1, r -> { Thread thread = new Thread(r); thread.setName("failAfter-%d"); thread.setDaemon(true); return thread; }); public static void main(String[] args) throws InterruptedException, ExecutionException { System.out.println("-------開始------"); final CompletableFuture<Object> oneSecondTimeout = failAfter(Duration.ofSeconds(2)) .exceptionally(xxx -> "超時"); List<Object> collect = Stream.of("1", "2", "3", "4", "5", "6", "7") .map(x -> CompletableFuture.anyOf(createTaskSupplier(x) , oneSecondTimeout)) .collect(Collectors.toList()) .stream() .map(CompletableFuture::join) .collect(Collectors.toList()); System.out.println("-------結束------"); System.out.println(collect.toString()); } public static CompletableFuture<String> createTaskSupplier(String x) { return CompletableFuture.supplyAsync(getStringSupplier(x)) .exceptionally(xx -> xx.getMessage()); } public static Supplier<String> getStringSupplier(String text) { return () -> { System.out.println("開始 " + text); if ("1".equals(text)) { throw new RuntimeException("運行時錯誤"); } try { if ("5".equals(text)) setSleepTime(20); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("停止 " + text); return text+"號"; }; } static void setSleepTime(int SleepTime) throws InterruptedException { TimeUnit.SECONDS.sleep(SleepTime); } public static <T> CompletableFuture<T> failAfter(Duration duration) { final CompletableFuture<T> promise = new CompletableFuture<>(); scheduler.schedule(() -> { final TimeoutException ex = new TimeoutException("Timeout after " + duration); return promise.completeExceptionally(ex); }, duration.toMillis(), TimeUnit.MILLISECONDS); return promise; } } //執行結果: -------開始------ 開始 1 開始 2 停止 2 開始 3 停止 3 開始 4 停止 4 開始 5 開始 6 停止 6 開始 7 停止 7 -------結束------ [java.lang.RuntimeException: 運行時錯誤, 2號, 3號, 4號, 超時, 6號, 7號]
幸運的是,JDK 9帶來了兩種新方法,可以為每個人提供渴望的功能 - 這對於確保在使用異步處理時的正確彈性至關重要。
在這篇超短篇文章中,嘗試幫助大家對這個新API方法的認識。
CompletableFuture#orTimeout
簡單地說,在調用上述方法之后,如果未在指定的超時內完成,將來會拋出ExecutionException。
一個簡單的例子:
CompletableFuture<Integer> future = CompletableFuture.supplyAsync(this::computeEndlessly) .orTimeout(1, TimeUnit.SECONDS); future.get(); // java.util.concurrent.ExecutionException after waiting for 1 second
由於設置了timeout為1秒,那么在get那里等待1秒后拋錯
CompletableFuture#completeOnTimeout
在這種情況下,我們可以在達到超時后返回默認值:
CompletableFuture<Integer> future = CompletableFuture.supplyAsync(this::computeEndlessly) .completeOnTimeout(42, 1, TimeUnit.SECONDS); Integer result = future.get(); // 42
超時1秒后不是報錯,而是返回了預設的42這個值,前提條件是你必須預設默認值。
就這么簡單,雖然我不一定喜歡我們總是被迫預先計算默認值的事實。