Java异步执行器CompletableFuture(Jdk9)改进

一、概述

CompletableFuture现有功能可以满足我们诉求。但当我们引入一些现实常见情况时,一些潜在的不足便暴露出来了。

Java9CompletableFuture类进行了一些更改。这些更改是作为JEP 266的一部分引入的,CompletableFuture正式提供了orTimeoutcompleteTimeout方法,来准确实现异步超时控制。

二、实例新添加的API

2.1. defaultExecutor()

public Executor defaultExecutor() {
	return ASYNC_POOL;
}

返回用于未指定执行程序的异步方法的默认Executor

这可以通过返回至少提供一个独立线程的Executor的子类来覆盖。

2.2. newIncompleteFuture()

public <U> CompletableFuture<U> newIncompleteFuture() {
	return new CompletableFuture<U>();
}

newIncompleteFuture,也称为“虚拟构造函数”,用于获取相同类型的新可完成未来实例。

此方法在子类化CompletableFuture时特别有用,主要是因为它在几乎所有返回新CompletionStage的方法中内部使用,允许子类控制这些方法返回的子类型。

2.3. copy()

public CompletableFuture<T> copy() {
    return uniCopyStage(this);
}

这个方法返回一个新的CompletableFuture

  • 当这个正常完成时,新的也正常完成
  • 当它以异常X异常完成时,新的一个也以异常完成,并以X作为原因的CompletionException

此方法可能用作“防御性复制”的一种形式,以防止客户端完成,同时仍然能够在CompletableFuture的特定实例上安排依赖操作。

2.4. minimalCompletionStage()

public CompletionStage<T> minimalCompletionStage() {
    return uniAsMinimalStage();
}

此方法返回一个新的CompletionStage,其行为方式与复制方法描述的方式完全相同,但是,此类新实例在每次尝试检索或设置解析值时都会引发UnsupportedOperationException
可以使用CompletionStageAPI上提供的toCompletableFuture方法检索包含所有可用方法的新CompletableFuture

2.5. completeAsync()

应使用completeAsync方法,使用供应商提供的值异步完成CompletableFuture

public CompletableFuture<T> completeAsync(Supplier<? extends T> supplier,
                                          Executor executor) {
    if (supplier == null || executor == null)
        throw new NullPointerException();
    executor.execute(new AsyncSupply<T>(this, supplier));
    return this;
}

public CompletableFuture<T> completeAsync(Supplier<? extends T> supplier) {
    return completeAsync(supplier, defaultExecutor());
}

这两个重载方法之间的区别在于存在第二个参数,可以在其中指定运行任务的执行器。如果未提供任何内容,则将使用默认执行器(由defaultExecutor方法返回)。

2.6. orTimeout()

public CompletableFuture<T> orTimeout(long timeout, TimeUnit unit) {
    if (unit == null)
        throw new NullPointerException();
    if (result == null)
        whenComplete(new Canceller(Delayer.delay(new Timeout(this),
                                                 timeout, unit)));
    return this;
}

使用TimeoutException异常解析CompletableFuture,除非它在指定的超时之前完成。

2.7.completeOnTimeout()

public CompletableFuture<T> completeOnTimeout(T value, long timeout,
                                              TimeUnit unit) {
    if (unit == null)
        throw new NullPointerException();
    if (result == null)
        whenComplete(new Canceller(Delayer.delay(
                                       new DelayedCompleter<T>(this, value),
                                       timeout, unit)));
    return this;
}

使用指定的值正常完成CompletableFuture,除非它在指定的超时之前完成。

三、静态API添加

还添加了一些实用程序方法

3.1. delayedExecutor

public static Executor delayedExecutor(long delay, TimeUnit unit,
                                       Executor executor) {
    if (unit == null || executor == null)
        throw new NullPointerException();
    return new DelayedExecutor(delay, unit, executor);
}

public static Executor delayedExecutor(long delay, TimeUnit unit) {
    if (unit == null)
        throw new NullPointerException();
    return new DelayedExecutor(delay, unit, ASYNC_POOL);
}

返回一个新的执行程序,该执行程序在给定延迟后(如果为非正值,则无延迟)向给定的基本执行程序提交任务。每个延迟在调用返回的执行程序的执行方法时开始。如果未指定执行器,则将使用默认执行器(ForkJoinPool.commonPool())。

3.2. completedStage与failedStage

public static <U> CompletionStage<U> completedStage(U value) {
    return new MinimalStage<U>((value == null) ? NIL : value);
}

public static <U> CompletionStage<U> failedStage(Throwable ex) {
    if (ex == null) throw new NullPointerException();
    return new MinimalStage<U>(new AltResult(ex));
}

此实用工具方法返回已解析的CompletionStage实例,这些实例要么正常完成,但有值(completeStage),要么异常完成(failedStage),但出现给定的异常。

3.3. failedFuture

public static <U> CompletableFuture<U> failedFuture(Throwable ex) {
    if (ex == null) throw new NullPointerException();
    return new CompletableFuture<U>(new AltResult(ex));
}

failedFuture方法添加了指定已完成的异常CompletableFuture实例的功能。

四、案例

在本节中,我们将展示一些有关如何使用一些新API的示例。

public class CompletableFutureTest {

    public void testOrTimeout() throws ExecutionException, InterruptedException {
        CompletableFuture<String> future = new CompletableFuture<>();
        future.orTimeout(1, TimeUnit.SECONDS);

        ExecutionException exception = assertThrows(ExecutionException.class, () -> {
            future.get(); // 任务未完成,预期抛出 TimeoutException
        });

        Throwable cause = exception.getCause();
        assertThrows(java.util.concurrent.TimeoutException.class, cause::getClass);
    }

    public void testCompleteOnTimeout() throws ExecutionException, InterruptedException {
        CompletableFuture<String> future = new CompletableFuture<>();
        future.completeOnTimeout("default", 1, TimeUnit.SECONDS);

        assertEquals("default", future.get()); // 预期结果为 "default"
    }

    public void testOrTimeoutAndCompleteOnTimeout() throws ExecutionException, InterruptedException {
        CompletableFuture<String> future = new CompletableFuture<>();
        future.completeAsync(() -> {
            try {
                TimeUnit.MILLISECONDS.sleep(500); // 模拟耗时任务
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return "result";
        });

        future.orTimeout(1, TimeUnit.SECONDS);
        future.completeOnTimeout("default", 1, TimeUnit.SECONDS);

        assertEquals("result", future.get()); // 预期结果为 "result"
    }

    public void testCompleteOnTimeoutBeforeCompletion() throws ExecutionException, InterruptedException {
        CompletableFuture<String> future = new CompletableFuture<>();
        future.completeAsync(() -> {
            try {
                TimeUnit.MILLISECONDS.sleep(500); // 模拟耗时任务
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return "result";
        });

        future.completeOnTimeout("default", 1, TimeUnit.SECONDS);

        assertEquals("result", future.get()); // 预期结果为 "result"
    }
}

五、总结

本文介绍了CompletableFuture现有功能的不足,以及新添加的API,并提供了示例用例。希望这些新API能够帮助我们更好地管理异步任务的执行。

六、拓展

JDK8异步工具类

如果我们使用的是JDK 9或以上,我们可以直接用JDK的实现来完成异步超时操作。那么JDK 8怎么办呢?

其实我们也可以根据上述逻辑简单实现一个工具类来辅助。

public class CompletableFutureTest {
    
    public CompletionStage<Response> orderCompletionStage() {
        Response resp = new Response();
        return CompleteFutureExtendUtil.orTimeout(CompletableFuture.supplyAsync(() -> {
                    try {
                        resp.setErrorCode("0");
                        resp.setValue("success");
                        resp.setData("order");
                        Thread.sleep(3000);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                    return resp;
                }), 2, TimeUnit.SECONDS)
                .exceptionally(throwable -> {
                    resp.setErrorCode("1");
                    resp.setValue("error");
                    resp.setData("order");
                    return resp;
                });
    }

    @Data
    class Response {
        private String errorCode;
        private String value;
        private Object data;
    }

    static class CompleteFutureExtendUtil {

        public static <T> CompletableFuture<T> orTimeout(CompletableFuture<T> future,
                                                         long timeout, TimeUnit unit) {
            final CompletableFuture<T> timeoutFuture = timeoutAfter(timeout, unit);
            // 哪个先完成 就apply哪一个结果
            return future.applyToEither(timeoutFuture, Function.identity());
        }

        public static <T> CompletableFuture<T> timeoutAfter(long timeout, TimeUnit unit) {
            CompletableFuture<T> result = new CompletableFuture<T>();
            // timeout 时间后 抛出TimeoutException 类似于sentinel / watcher
            Delayer.delayer.schedule(() -> result.completeExceptionally(new TimeoutException()), timeout, unit);
            return result;
        }

        /**
         * 单例延迟调度器,仅用于启动和取消任务,一个线程就足够
         */
        static final class Delayer {
            static ScheduledFuture<?> delay(Runnable command, long delay,
                                            TimeUnit unit) {
                return delayer.schedule(command, delay, unit);
            }

            static final class DaemonThreadFactory implements ThreadFactory {
                @Override
                public Thread newThread(Runnable r) {
                    Thread t = new Thread(r);
                    t.setDaemon(true);
                    t.setName("CompletableFutureDelayScheduler");
                    return t;
                }
            }

            static final ScheduledThreadPoolExecutor delayer;

            // 注意,这里使用一个线程就可以搞定 因为这个线程并不真的执行请求 而是仅仅抛出一个异常
            static {
                (delayer = new ScheduledThreadPoolExecutor(
                        1, new DaemonThreadFactory())).
                        setRemoveOnCancelPolicy(true);
            }
        }
    }
}
posted @ 2025-04-26 13:14  夏尔_717  阅读(180)  评论(0)    收藏  举报