Java中的CompletableFuture超時使用


我喜歡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這個值,前提條件是你必須預設默認值。

 

就這么簡單,雖然我不一定喜歡我們總是被迫預先計算默認值的事實。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM