CompletableFuture详解

文章目录

在这里插入图片描述

前言

最近有一个接口需求,需要从多个服务获取数据返回给前端,如果串行一个一个获取的话,假如每个服务1s,5个服务就要5s,服务越多时间越久。

在这里插入图片描述

这时候,我们可以将串行获取改为并行获取,大大降低了耗时。

在这里插入图片描述

并行获取虽然提高了效率,但也有一个问题,如果某个服务异常阻塞,这个接口会一直等到这个服务超时返回(根据设置的超时时间),这时候就需要一个功能,那就是超时获取。

超时的并行获取,也是并行去获取数据,但我们规定了一个时间,比如1s,那么获取数据的操作就会在1s返回,没有在规定时间内返回的数据将会被丢弃。这样虽然数据会不全,但是总比一直阻塞没有结果好。

在这里插入图片描述

实现超时获取的两种方法如下。

Callable+Future实现

public class DataService {

    private static final ExecutorService executorService = Executors.newFixedThreadPool(3);

    public static void main(String[] args) {
        // 创建多个获取数据的任务
        List<Callable<String>> tasks = new ArrayList<>();
        tasks.add(new Task("1", 1));
        tasks.add(new Task("2", 1));
        tasks.add(new Task("3", 3));

        // 获取数据
        String result = fetchData(tasks);

        // 打印结果
        System.out.println("Result: " + result);

        // 关闭线程池
        executorService.shutdown();
    }

    public static String fetchData(List<Callable<String>> tasks) {
        try {
            // 调用invokeAll方法,等待1秒内返回结果
            List<Future<String>> futures = executorService.invokeAll(tasks, 1, TimeUnit.SECONDS);
            // 获取所有任务的执行结果
            List<String> results = new ArrayList<>();
            for (Future<String> future : futures) {
                if (future.isDone() && !future.isCancelled()) {
                    results.add(future.get());
                }
            }
            return String.join(",", results);
        } catch (InterruptedException e) {
            // 超时异常,可以记录日志等
            System.out.println("Timeout: " + e.getMessage());
        } catch (ExecutionException e) {
            // 其他异常处理
            e.printStackTrace();
        }
        // 如果超时或发生异常,返回一个默认值或者进行其他处理
        return "Default Result";
    }

    static class Task implements Callable<String> {

        String serviceName;
        long timeout;

        Task(String serviceName, long timeout) {
            this.serviceName = serviceName;
            this.timeout = timeout;
        }

        @Override
        public String call() throws Exception {
            // 模拟从服务获取数据的逻辑
            try {
                Thread.sleep(TimeUnit.SECONDS.toMillis(timeout));
                return "Data from Service " + serviceName;
            } catch (InterruptedException e) {
                e.printStackTrace();
                return null;
            }
        }
    }
}
// 输出
Result: Data from Service 1,Data from Service 2

在上述代码中,我们使用Callable创建获取数据的任务,然后使用 invokeAll 提交所有任务,并设置了1秒的超时时间,等待任务的完成,如果任务都在1s内完成,会立即返回。对于每个任务,检查它是否已完成,如果已完成则获取结果,如果未完成或异常则记录日志。

CountDownLatch实现

public class DataService {
    private static final ExecutorService executorService = Executors.newFixedThreadPool(3);

    public static void main(String[] args) {

        CountDownLatch countDownLatch = new CountDownLatch(3);
        List<String> result = new CopyOnWriteArrayList<>();
        executorService.submit(new Task("1", 500, result, countDownLatch));
        executorService.submit(new Task("2", 500, result, countDownLatch));
        executorService.submit(new Task("3", 1200, result, countDownLatch));

        try {
            // 等待所有任务完成,最长等待时间为1秒
            countDownLatch.await(1, TimeUnit.SECONDS);
        } catch (InterruptedException e) {
            // 处理中断异常
            throw new RuntimeException("Task execution interrupted", e);
        }
        // 打印结果
        System.out.println("Results: " + result);

        // 关闭线程池
        executorService.shutdown();
    }

    static class Task implements Runnable {

        String serviceName;
        long timeout;
        List<String> result;
        CountDownLatch countDownLatch;

        public Task(String serviceName, long timeout, List<String> result, CountDownLatch countDownLatch) {
            this.serviceName = serviceName;
            this.timeout = timeout;
            this.result = result;
            this.countDownLatch = countDownLatch;
        }

        @Override
        public void run() {
            // 模拟从服务获取数据的逻辑
            try {
                Thread.sleep(TimeUnit.MILLISECONDS.toMillis(timeout));
                result.add("Data from Service " + serviceName);
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                countDownLatch.countDown();
            }
        }
    }
}
// 输出
Results: [Data from Service 1, Data from Service 2]

在上述代码中,我们使用CountDownLatch实现超时获取功能,使用线程池提交任务,任务完成时会调用countDown方法,将计数器减1,countDownLatch.await会等待1s,如果三个任务很快执行完成,都执行了countDown方法,那么await方法会立即返回。

新需求

上面我们已经完成了超时获取的功能,该接口会在1s内返回数据,这样可以提升用户的体验,超过1s的服务接口我们可以进行优化。

这时候又来了一个新的需求,并行获取服务1、2、3的数据,然后根据服务1返回的数据去获取服务4的数据,再将四个服务的数据全部返回。

在这里插入图片描述

Future实现

public class DataService {
    private static final ExecutorService executorService = Executors.newFixedThreadPool(3);
    
    public static void main(String[] args) {
        // 使用Future获取服务1、服务2、服务3的数据
        Future<String> result1 = executorService.submit(new Task("Service1"));
        Future<String> result2 = executorService.submit(new Task("Service2"));
        Future<String> result3 = executorService.submit(new Task("Service3"));
        Future<String> result4 = executorService.submit(new Task4(result1));

        try {
            // 获取服务数据
            String data1 = result1.get();
            String data2 = result2.get();
            String data3 = result3.get();
            String data4 = result4.get();

            System.out.println(data1);
            System.out.println(data2);
            System.out.println(data3);
            System.out.println(data4);

        } catch (InterruptedException | ExecutionException e) {
            e.printStackTrace();
        } finally {
            executorService.shutdown();
        }
    }

    static class Task implements Callable<String> {
        private final String serviceName;

        Task(String serviceName) {
            this.serviceName = serviceName;
        }

        @Override
        public String call() throws Exception {
            return "Data from " + serviceName;
        }
    }

    static class Task4 implements Callable<String> {

        private final Future<String> future;

        Task4(Future<String> future) {
            this.future = future;
        }

        @Override
        public String call() throws Exception {
            while (!future.isDone()) {
                try {
                    Thread.sleep(100); // 等待一段时间
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            return "Data from service4 for " + future.get();
        }
    }
}

上述代码使用了 Future 来处理异步任务的结果,但存在一些不足之处:

  1. 使用 get 方法会阻塞当前线程,直到服务的数据返回,这样可能导致整体程序的性能下降。
  2. 循环调用 isDone 进行检查,会带来一定的轮询开销,造成CPU空转,导致不必要的上下文切换。
  3. 不支持异步任务的编排组合,无法方便地进行多个异步任务之间的链式操作,在处理多个异步任务时可能需要使用较为繁琐的手动管理方式。
  4. 不支持回调机制,无法轻松地在任务完成时触发后续的操作。

CompletableFuture介绍

为了弥补Future的不足,Java 8 引入了 CompletableFuture 类,它是 Future 的扩展,并提供了更强大的异步编程工具。CompletableFuture 具有更灵活的非阻塞操作、更丰富的异常处理、支持链式操作、更好的组合多个任务等特性,使得异步编程更加方便和强大。

在这里插入图片描述

CompletableFuture同时实现了FutureCompletionStageFuture的主要接口在《一次搞定Thread、Runable、Callable、Future、FutureTask》介绍过了,我们主要看CompletionStage

在这里插入图片描述

可以看到CompletionStage有很多方法,并且支持函数式编程,其被设计为用于组合和处理异步操作,为开发者提供了更灵活和强大的异步编程工具。

函数式接口

在介绍CompletableFuture的方法之前,我们先来复习一下函数式接口,因为其中使用了大量的函数式接口。

函数式接口只包含了一个抽象方法,该接口被@FunctionalInterface修饰,函数式接口为我们提供了函数式编程的支持,让我们能够更轻松地写出简洁而强大的代码。下面介绍五个常用的函数式接口。

  • Runnable,run方法,不能接收参数,无返回值。
@FunctionalInterface
public interface Runnable {
    public abstract void run();
}
  • Function,apply方法,接收一个参数,有返回值。
@FunctionalInterface
public interface Function<T, R> {
    R apply(T t);
}
  • Consumer,accept方法,接收一个参数,无返回值。
@FunctionalInterface
public interface Consumer<T> {
    void accept(T t);
}
  • Supplier,get方法,不接收参数,有返回值。
@FunctionalInterface
public interface Supplier<T> {
    T get();
}
  • BiConsumer,accept方法,接收两个参数,无返回值。
@FunctionalInterface
public interface BiConsumer<T, U> {
    void accept(T t, U u);
}

常用方法

创建CompletableFuture

public static CompletableFuture<Void> runAsync(Runnable runnable);

public static CompletableFuture<Void> runAsync(Runnable runnable, Executor executor);

public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier);

public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier, Executor executor);

runAsyncsupplyAsync的区别主要在于接收的函数式接口不同,Runnable接口没有返回值,Supplier接口有返回值。executor参数为用于执行任务的线程池,如果不传,使用默认的ForkJoinPool线程池。

推荐使用自定义线程池

为什么推荐使用自定义线程池?我们来看一下源码

public static CompletableFuture<Void> runAsync(Runnable runnable) {
	return asyncRunStage(asyncPool, runnable);
}

// 判断计算机处理器的核心数是否大于1
private static final boolean useCommonPool = (ForkJoinPool.getCommonPoolParallelism() > 1);

// 核心数大于1则使用ForkJoinPool线程池,否则每次都新建一个线程
private static final Executor asyncPool = useCommonPool ?
    ForkJoinPool.commonPool() : new ThreadPerTaskExecutor();

// 创建一个线程执行
static final class ThreadPerTaskExecutor implements Executor {
    public void execute(Runnable r) { new Thread(r).start(); }
}

如果你的电脑是2核,ForkJoinPool.getCommonPoolParallelism()获取的值有可能是1,这种情况下,CompletableFuture 将会为每个任务创建一个新的线程。每个异步任务都会独占一个线程,会导致线程的频繁创建和销毁。

另外如果使用默认的ForkJoinPool,会与其他使用ForkJoinPool的功能共享,导致线程争用,影响性能。

使用自定义线程池,可以更好地控制线程的生命周期和优化执行性能。

获取结果

public T get() throws InterruptedException, ExecutionException;

public T get(long timeout, TimeUnit unit)
        throws InterruptedException, ExecutionException, TimeoutException;

public T join();

// 计算完成返回结果,没有完成返回指定值
public T getNow(T valueIfAbsent);

get() 方法声明了 InterruptedExceptionExecutionException 异常,因此需要显式地处理异常。在等待任务完成的过程中,如果线程被中断,会抛出中断异常InterruptedException

get(long timeout, TimeUnit unit)方法与get不同在于指定了等待时间,超时未返回抛出TimeoutException异常。

join()方法没有声明异常,执行过程中发生的异常被包装在 CompletionException 中,不需要显式地处理异常。

在不需要特别处理异常的情况下使用 join方法。有特殊的异常处理需求,或者需要处理中断,使用 get(long timeout, TimeUnit unit) 方法,指定时间内返回。

异步回调方法

thenRun

public CompletableFuture<Void> thenRun(Runnable action);

public CompletableFuture<Void> thenRunAsync(Runnable action);

public CompletableFuture<Void> thenRunAsync(Runnable action, Executor executor);

thenRun 方法接收一个 Runnable 参数,该 Runnable 会在前一个异步任务正常完成后执行。不处理前一个阶段的结果,也不返回任何结果。

CompletableFuture<Void> future = CompletableFuture
    .runAsync(() -> System.out.print("Hello "))
    .thenRun(() -> System.out.println("CompletableFuture"));

future.join();
// 输出
Hello CompletableFuture
thenRun和thenRunAsync区别

在这里插入图片描述

  • runAsync没有传入自定义线程池,默认都使用ForkJoinPool
public static void main(String[] args) {

    CompletableFuture<Void> future1 = CompletableFuture.runAsync(() -> {
        System.out.println("runAsync future1 " + Thread.currentThread().getName());
    }).thenRun(() -> {
        System.out.println("thenRun future1 " + Thread.currentThread().getName());
    });

    CompletableFuture<Void> future2 = CompletableFuture.runAsync(() -> {
        System.out.println("runAsync future2 " + Thread.currentThread().getName());
    }).thenRunAsync(() -> {
        System.out.println("thenRunAsync future2 " + Thread.currentThread().getName());
    });

    CompletableFuture.allOf(future1, future2).join();

}
// 输出
runAsync future1 ForkJoinPool.commonPool-worker-1
thenRun future1 ForkJoinPool.commonPool-worker-1
runAsync future2 ForkJoinPool.commonPool-worker-1
thenRunAsync future2 ForkJoinPool.commonPool-worker-1
  • runAsync传入自定义线程池,thenRun使用同一个线程池,thenRunAsync使用默认ForkJoinPool
public static void main(String[] args) {
    ExecutorService executorService = Executors.newFixedThreadPool(3);

	// runAsync传入自定义线程池
    CompletableFuture<Void> future1 = CompletableFuture.runAsync(() -> {
        System.out.println("runAsync future1 " + Thread.currentThread().getName());
    }, executorService).thenRun(() -> {
        System.out.println("thenRun future1 " + Thread.currentThread().getName());
    });
    
	// runAsync传入自定义线程池,thenRunAsync没传入自定义线程池
    CompletableFuture<Void> future2 = CompletableFuture.runAsync(() -> {
        System.out.println("runAsync future2 " + Thread.currentThread().getName());
    }, executorService).thenRunAsync(() -> {
        System.out.println("thenRunAsync future2 " + Thread.currentThread().getName());
    });
    
	// runAsync传入自定义线程池,thenRunAsync传入自定义线程池
    CompletableFuture<Void> future3 = CompletableFuture.runAsync(() -> {
        System.out.println("runAsync future3 " + Thread.currentThread().getName());
    }, executorService).thenRunAsync(() -> {
        System.out.println("thenRunAsync future3 " + Thread.currentThread().getName());
    }, executorService);

    CompletableFuture.allOf(future1, future2, future3).join();

    executorService.shutdown();
}
// 输出
runAsync future1 pool-1-thread-1
thenRun future1 pool-1-thread-1
runAsync future2 pool-1-thread-2
thenRunAsync future2 ForkJoinPool.commonPool-worker-1
runAsync future3 pool-1-thread-3
thenRunAsync future3 pool-1-thread-1
  • 可能处理太快,系统优化切换原则,直接使用主线程处理
public static void main(String[] args) {

    CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
        System.out.println("runAsync future1 " + Thread.currentThread().getName());
    }).thenRun(() -> {
        System.out.println("thenRun future1 " + Thread.currentThread().getName());
    });

    future.join();
}
// 输出
runAsync future1 ForkJoinPool.commonPool-worker-1
thenRun future1 main

其他方法的同步方法和Async方法的区别,与thenRun一致。

thenApply

public <U> CompletableFuture<U> thenApply(Function<? super T,? extends U> fn);

thenApply 方法接收Function参数,用于处理上一个任务的正常结果,并返回一个新的结果。

CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> "Hello")
                .thenApply(result -> result + ", CompletableFuture!");

System.out.println(future.join());  // 输出:Hello, CompletableFuture!

thenAccept

public CompletableFuture<Void> thenAccept(Consumer<? super T> action);

thenAccept 方法接收Consumer参数,用于处理上一个任务的正常结果,但不返回新的结果。

CompletableFuture<Void> future = CompletableFuture.supplyAsync(() -> "Hello")
                .thenAccept(result -> System.out.println(result + ", CompletableFuture!"));

future.join();  // 输出:Hello, CompletableFuture!

thenCombine

public <U,V> CompletableFuture<V> thenCombine(CompletionStage<? extends U> other, BiFunction<? super T,? super U,? extends V> fn);

thenCombine 用于组合两个 CompletableFuture 的结果,并返回一个新的结果。第一个参数为另一个CompletableFuture,第二个参数为BiFunction函数接口,接收两个参数,返回新的结果。

thenCombine会在两个任务都执行完成后,把两个任务的结果合并。两个任务是并行执行的,它们之间并没有先后依赖顺序。

CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> "Hello")
                        .thenCombine(CompletableFuture.supplyAsync(() -> "CompletableFuture"), (s1, s2) -> s1 + " " + s2);

System.out.println(combinedFuture.join());  // 输出:Hello CompletableFuture

whenComplete

上面四种方法,都只能处理正常的结果,即只有正常完成才会执行后续的回调方法。而whenComplete无论正常完成还是抛出异常,都会执行回调方法,允许你处理任务的结果或异常,进行相应的操作。

public CompletableFuture<T> whenComplete(BiConsumer<? super T, ? super Throwable> action);

whenComplete接收BiConsumer函数式接口,该接口接收两个参数,一个是正常完成的结果,另一个是任务抛出的异常,但不返回新的结果。

CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
    if (true) {
        throw new RuntimeException("CompletableFuture error");
    }
    return "hello";
}).whenComplete((res, ex) -> {
    if (ex != null) {
        System.out.println(ex.getMessage());
    } else {
        System.out.println(res + " CompletableFuture!");
    }
});

future.join();
// 输出:java.lang.RuntimeException: CompletableFuture error
抛出异常

异常处理

exceptionally

public CompletableFuture<T> exceptionally(Function<Throwable, ? extends T> fn);

exceptionally 用于处理异常情况,该回调方法会在 CompletableFuture 发生异常时触发执行。可以捕获异常并提供默认值或处理异常情况。

CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> 10 / 0)
        .exceptionally(ex -> {
            System.out.println("Exception: " + ex.getMessage());
            return 0;
        });

System.out.println(future.join());  
// 输出
Exception: java.lang.ArithmeticException: / by zero
0

handle

public <U> CompletableFuture<U> handle(BiFunction<? super T, Throwable, ? extends U> fn);

handle 方法会在 CompletableFuture 正常完成或发生异常时触发执行,可以处理正常结果和异常情况。参数是一个 BiFunction,该函数接收计算的结果和异常信息作为参数,返回一个新的值。

CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> 10 / 2)
        .handle((result, ex) -> {
            if (ex != null) {
                System.out.println("Exception: " + ex.getMessage());
                return 0;
            } else {
                return result * 2;
            }
        });

System.out.println(future.join());  // 输出:10

对计算速度进行选用

假如我们想要实现 任务1 和 任务2 中的任意一个执行完后就执行 任务3 的话,可以使用applyToEither acceptEither()

public <U> CompletableFuture<U> applyToEither(
        CompletionStage<? extends T> other, Function<? super T, U> fn);

public CompletableFuture<Void> acceptEither(
        CompletionStage<? extends T> other, Consumer<? super T> action);

上面两个方法第一个参数都是另一个异步任务CompletableFuture,区别在于第二个参数。

applyToEither接收Function函数,接收一个参数,返回一个新的值;

acceptEither接收Consumer函数,接收一个参数,没有返回值。

public static void main(String[] args) {
    
    CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> {
        try {
            Thread.sleep(500);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("执行task1");
        return "task1完成";
    });

    CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> {
        System.out.println("执行task2");
        return "task2完成";
    });

    CompletableFuture<String> future = future1.applyToEither(future2, (res) -> res + ",执行task3");

    
    CompletableFuture.allOf(future1, future2, future).join();
}
// 输出
执行task2
task2完成,执行task3
执行task1

并行执行异步任务

allof

public static CompletableFuture<Void> allOf(CompletableFuture<?>... cfs);

当我们有多个任务,任务之间没有关联关系,我们可以使用 allOf 方法并行执行,allOf会等待所有的任务完成后返回。

public static void main(String[] args) {

    CompletableFuture<Void> future1 = CompletableFuture.runAsync(() -> System.out.println("task1完成"));

    CompletableFuture<Void> future2 = CompletableFuture.runAsync(() -> System.out.println("task2完成"));

    CompletableFuture.allOf(future1, future2).join();
}
// 输出
task1完成
task2完成

anyof

public static CompletableFuture<Object> anyOf(CompletableFuture<?>... cfs);

anyOf 方法则不会等待所有的任务都运行完成之后再返回,只要有一个执行完成就会返回。

public static void main(String[] args) {

    CompletableFuture<Void> future1 = CompletableFuture.runAsync(() -> {
        try {
            Thread.sleep(500);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("task1完成");
    });

    CompletableFuture<Void> future2 = CompletableFuture.runAsync(() -> System.out.println("task2完成"));


    CompletableFuture.anyOf(future1, future2).join();
}
// 输出
task2完成

小结

上面介绍了CompletableFuture的常用方法,根据这些方法,我们来实现上面的需求,根据服务1返回的数据去请求服务4,再将四个服务的数据全部返回。

public class DataService {

    private final static ExecutorService executorService = Executors.newFixedThreadPool(3);

    public static void main(String[] args) {

        List<String> result = new CopyOnWriteArrayList<>();

        CompletableFuture<Void> future1 = CompletableFuture.supplyAsync(() -> {
            result.add("data from service1");
            return "result1";
        }, executorService).thenAccept(res -> {
            result.add("data from service4 dependent on " + res);
        });

        CompletableFuture<Void> future2 = CompletableFuture.runAsync(() -> result.add("data from service2"), executorService);

        CompletableFuture<Void> future3 = CompletableFuture.runAsync(() -> result.add("data from service3"), executorService);

		try {
            CompletableFuture.allOf(future1, future2, future3).get(1, TimeUnit.SECONDS);
        } catch (InterruptedException | ExecutionException | TimeoutException e) {
            // 处理异常
            e.printStackTrace();
        }

        System.out.println(result);

        executorService.shutdown();
    }
}
// 输出
[data from service1, data from service4 dependent on result1, data from service2, data from service3]

上述代码使用CompletableFuture实现了该需求,我们来总结一下CompletableFuture的优点。

  1. 支持异步函数式编程,实现优雅,代码简洁,易于维护。
  2. 支持链式任务,任务回调,可以轻松地组织不同任务的运行顺序、规则以及方式。

源码分析

volatile Object result;

volatile Completion stack;

CompletableFuture中维护了两个属性,result 字段用于存储任务的结果或异常,stack字段是一个Completion内部类,用于构建一个栈,存储完成时需要执行的回调操作。

runAsync和supplyAsync

runAsyncsupplyAsync的主要区别在于使用不同的内部类去执行任务。

runAsync使用AsyncRun内部类,在完成任务时,使用completeNull方法将CompletableFuture的结果设置为内部类new AltResult(null),结果为null。

supplyAsync使用AsyncSupply内部类,完成任务时,使用completeValue方法将结果设置为函数式接口Supplier返回的结果。

最后两者都会调用postComplete方法,触发回调链上的操作,进行下一任务。

AsyncRun

static final class AsyncRun extends ForkJoinTask<Void>
            implements Runnable, AsynchronousCompletionTask {
    
    // ...省略

    public void run() {
        CompletableFuture<Void> d; Runnable f;
        if ((d = dep) != null && (f = fn) != null) {
            dep = null; fn = null;
            if (d.result == null) {
                try {
                    f.run();
                    // CAS设置结果为内部类AltResult(null)
                    d.completeNull();
                } catch (Throwable ex) {
                    d.completeThrowable(ex);
                }
            }
            // 任务结束后,执行所有依赖此任务的其他任务
            d.postComplete();
        }
    }
}

AsyncSupply

static final class AsyncSupply<T> extends ForkJoinTask<Void>
            implements Runnable, AsynchronousCompletionTask {

    // ...省略

    public void run() {
        CompletableFuture<T> d; Supplier<T> f;
        if ((d = dep) != null && (f = fn) != null) {
            dep = null; fn = null;
            if (d.result == null) {
                try {
                    // CAS设置结果为任务返回结果
                    d.completeValue(f.get());
                } catch (Throwable ex) {
                    d.completeThrowable(ex);
                }
            }
            // 任务结束后,执行所有依赖此任务的其他任务
            d.postComplete();
        }
    }
}

postComplete

final void postComplete() {
    CompletableFuture<?> f = this; Completion h;
    // 循环遍历回调链上的任务,执行回调
    while ((h = f.stack) != null ||
           (f != this && (h = (f = this).stack) != null)) {
        CompletableFuture<?> d; Completion t;
        // 从头遍历stack,并更新头元素
        if (f.casStack(h, t = h.next)) {
            if (t != null) {
                //  如果f不是当前CompletableFuture,则将它的头结点压入到当前CompletableFuture的stack中,使树形结构变成链表结构,避免递归层次过深
                if (f != this) {
                    pushStack(h);
                    continue;
                }
                // 如果是当前CompletableFuture, 解除头节点与栈的联系
                h.next = null;
            }
            f = (d = h.tryFire(NESTED)) == null ? this : d;
        }
    }
}

每个CompletableFuture持有一个Completion栈stack, 每个Completion持有一个CompletableFuture -> dep, 如此递归循环下去,是层次很深的树形结构,所以想办法将其变成链表结构。

thenRun

public CompletableFuture<Void> thenRun(Runnable action) {
    return uniRunStage(null, action);
}

private CompletableFuture<Void> uniRunStage(Executor e, Runnable f) {
    if (f == null) throw new NullPointerException();
    CompletableFuture<Void> d = new CompletableFuture<Void>();
    // 检查依赖的那个任务是否完成,没有完成返回false,推入栈
    if (e != null || !d.uniRun(this, f, null)) {
        UniRun<T> c = new UniRun<T>(e, d, this, f);
        push(c);
        c.tryFire(SYNC);
    }
    return d;
}

final boolean uniRun(CompletableFuture<?> a, Runnable f, UniRun<?> c) {
        Object r; Throwable x;
    // 被依赖的任务未完成,结果为null,返回false
    if (a == null || (r = a.result) == null || f == null)
        return false;
    // 被依赖的任务完成了
    if (result == null) {
        if (r instanceof AltResult && (x = ((AltResult)r).ex) != null)
            completeThrowable(x, r);
        else
            try {
                // 判断任务是否能被执行
                if (c != null && !c.claim())
                    return false;
                // 执行任务
                f.run();
                // 设置任务结果
                completeNull();
            } catch (Throwable ex) {
                completeThrowable(ex);
            }
    }
    return true;
}

thenRun 方法中,通过 uniRunStage 方法创建了一个新的 CompletableFuture<Void> 对象 d,然后尝试直接执行当前任务,如果无法直接执行(上一任务未完成),则创建一个 UniRun 对象 c,表示 thenRun 的回调。

UniRun

static final class UniRun<T> extends UniCompletion<T,Void> {
    Runnable fn;
    UniRun(Executor executor, CompletableFuture<Void> dep,
           CompletableFuture<T> src, Runnable fn) {
        super(executor, dep, src); this.fn = fn;
    }
    final CompletableFuture<Void> tryFire(int mode) {
        CompletableFuture<Void> d; // 依赖的任务
        CompletableFuture<T> a;  // 被依赖任务
        // 异步模式(mode = 1),就不判断任务是否结束
        if ((d = dep) == null ||
            !d.uniRun(a = src, fn, mode > 0 ? null : this))
            return null;
        dep = null; src = null; fn = null;
        // 完成之后的处理
        return d.postFire(a, mode);
    }
}

postFire

final CompletableFuture<T> postFire(CompletableFuture<?> a, int mode) {
    // 存在被依赖任务,且回调链上还有回调任务,执行回调
    if (a != null && a.stack != null) {
        // mode小于0表示嵌套执行,保证回调链的顺序执行,清除依赖任务的回调链
        if (mode < 0 || a.result == null)
            a.cleanStack();
        else
            // 执行依赖任务的完成操作,触发回调
            a.postComplete();
    }
    // 依赖任务完成,且回调链上有任务
    if (result != null && stack != null) {
        // 嵌套模式,直接返回自身(树 -> 链表,避免过深的递归调用)
        if (mode < 0)
            return this;
        else
            // 执行完成操作,触发回调链上的任务
            postComplete();
    }
    return null;
}

cleanStack

// 过滤掉已经死掉的结点
final void cleanStack() {
    // q指针从头结点开始,向右移动,p要么为空,要么指向最后一个活着的节点
    for (Completion p = null, q = stack; q != null;) {
        Completion s = q.next;
        // p活着,q指向p,p指向下一节点
        if (q.isLive()) {
            p = q;
            q = s;
        }
        else if (p == null) {
            // q死掉且p为空,去掉头结点,重新开始
            casStack(q, s);
            q = stack;
        }
        else {
            // q死掉,p节点的next为死节点的下一节点
            p.next = s;
            if (p.isLive())
                q = s;
            else {
                p = null;
                q = stack;
            }
        }
    }
}

在这里插入图片描述

allof

public static CompletableFuture<Void> allOf(CompletableFuture<?>... cfs) {
    return andTree(cfs, 0, cfs.length - 1);
}

static CompletableFuture<Void> andTree(CompletableFuture<?>[] cfs, int lo, int hi) {
    CompletableFuture<Void> d = new CompletableFuture<Void>();
    // 为空返回空结果
    if (lo > hi)
        d.result = NIL;
    else {
        CompletableFuture<?> a, b;
        // 找到中间位置
        int mid = (lo + hi) >>> 1;
        // 构建左子树a和右子树b
        if ((a = (lo == mid ? cfs[lo] :
                  andTree(cfs, lo, mid))) == null ||
            (b = (lo == hi ? a : (hi == mid+1) ? cfs[hi] :
                  andTree(cfs, mid+1, hi)))  == null)
            throw new NullPointerException();
        // 尝试完成任务,无法完成,创建BiRelay对象作为中继
        if (!d.biRelay(a, b)) {
            BiRelay<?,?> c = new BiRelay<>(d, a, b);
            a.bipush(b, c);
            // 触发执行
            c.tryFire(SYNC);
        }
    }
    return d;
}

为什么要构建二叉树?

假设有一个任务Z(虚拟的,什么都不做),依赖一组任务[A, B, C, D],任务Z完成则表示所有任务完成。如果这组任务是数组结构或者链表结构,需要不断地轮询遍历数组或链表,这是非常消耗性能的。

使用二叉树的话,Z只要保证Z1和Z2都完成了就行,而Z11,Z12则分别保证了A-D任务都完成。动态规划加二叉树,时间复杂度只是logn级别。

在这里插入图片描述

参考:https://www.cnblogs.com/aniao/p/aniao_cf.html

  • 32
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值