簡體   English   中英

Java 8 CompletableFuture,流和超時

[英]Java 8 CompletableFuture , Stream and Timeouts

我正在嘗試使用CompletableFutureStream同時處理一些數據,到目前為止,我有:

public static void main(String[] args) throws InterruptedException, ExecutionException {
    System.out.println("start");

    List<String> collect = Stream.of("1", "2", "3", "4", "5",
            "6", "7")
            .map(x -> CompletableFuture.supplyAsync(getStringSupplier(x)))
            .collect(Collectors.toList())
            .stream()
            .map(CompletableFuture::join)
            .collect(Collectors.toList());
    System.out.println("stop out!");
}


public static Supplier<String> getStringSupplier(String text) {
    return () -> {

        System.out.println("start " + text);
        try {
            TimeUnit.SECONDS.sleep(2);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("stop " + text);
        return "asd" + text;
    };
}

輸出很好:

start start 1 start 4 start 3 start 2 start 5 start 6 start 7 stop 4 stop 1 stop 5 stop 2 stop 6 stop 3 stop 7 stop out!

但是現在我想為該工作添加超時。 可以說應在1秒后將其取消。 並返回null或其他一些值來collect列表。 (我希望一些表示原因的值)。

我該如何實現?

預先感謝您的幫助。

我發現了這樣做的方法:

 private static final ScheduledExecutorService scheduler =
        Executors.newScheduledThreadPool(
                1,
                new ThreadFactoryBuilder()
                        .setDaemon(true)
                        .setNameFormat("failAfter-%d")
                        .build());

public static void main(String[] args) throws InterruptedException, ExecutionException {
    System.out.println("start");
    final CompletableFuture<Object> oneSecondTimeout = failAfter(Duration.ofSeconds(1))
            .exceptionally(xxx -> "timeout exception");
    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("stop out!");
    System.out.println(collect);
}

public static CompletableFuture<String> createTaskSupplier(String x) {
    return CompletableFuture.supplyAsync(getStringSupplier(x))
            .exceptionally(xx -> "PROCESSING ERROR : " + xx.getMessage());
}


public static Supplier<String> getStringSupplier(String text) {
    return () -> {

        System.out.println("start " + text);
        try {
            TimeUnit.MILLISECONDS.sleep(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        if (text.equals("1")) {
            throw new RuntimeException("LOGIC ERROR");
        }
        try {
            if (text.equals("7"))
                TimeUnit.SECONDS.sleep(2);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("stop " + text);
        return "result " + text;
    };
}

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(), MILLISECONDS);
    return promise;
}

它返回:

 start
 start 1
 start 3
 start 4
 start 2
 start 5
 start 6
 start 7
 stop 6
 stop 4
 stop 3
 stop 5
 stop 2
 stop out!
 [PROCESSING ERROR : java.lang.RuntimeException: LOGIC ERROR, result 2, result 3, result 4, result 5, result 6, timeout exception]`

您對此有何看法,您能否發現該解決方案的任何缺陷?

對於不限於Java 8的其他人,可以使用Java 9中引入的completeOnTimeout方法。

List<String> collect = Stream.of("1", "2", "3", "4", "5", "6", "7")
        .map(x -> CompletableFuture.supplyAsync(getStringSupplier(x))
                .completeOnTimeout(null , 1, SECONDS))
        .filter(Objects::nonNull)
        .collect(toList())
        .stream()
        .map(CompletableFuture::join)
        .collect(toList());

您可以將作業包裝在另一個CompletableFuture中,如果超過給定的時間,它將發出TimeoutException。 如果要特別處理,可以將TimeoutException catch塊分開。

    List<String> collect = null;
    try {
        collect = CompletableFuture.supplyAsync(() ->
                Stream.of("1", "2", "3", "4", "5",
                        "6", "7")
                        .map(x -> CompletableFuture.supplyAsync(getStringSupplier(x)))
                        .collect(Collectors.toList())
                        .stream()
                        .map(CompletableFuture::join)
                        .collect(Collectors.toList())
        ).get(5, TimeUnit.SECONDS);
    } catch (InterruptedException | ExecutionException | TimeoutException e) {
        e.printStackTrace();
        //separate out the TimeoutException if you want to handle it differently 

    }

    System.out.println(collect); //would be null in case of any exception

您可以嘗試使用具有執行程序參數(CompletableFuture.supplyAsync(getStringSupplier(x),timeoutExecutorService))的 CompletableFuture的重載supplyAsync方法,並可以引用timeoutExecutorService的鏈接

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM