CompletableFuture使用详解

目录

一、runAsync 

1、runAsync(Runnable)

2、runAsync(Runnable, Executor) 

二、supplyAsync

1、supplyAsync(Supplier)

2、supplyAsync(Supplier , Executor)

三、CompletableFuture中 get 与 join的区别

四、thenApply方法 

1、thenApply(Function) 

五、handle方法 

 1、handle(BiFunction fn),>

2、handleAsync(BiFunction fn,Executor) ,>

 六、thenCombine方法

1、thenCombine

2、thenCombineAsync 

七、thenCompose方法

1、thenCompose

八、allOf / anyOf


普通情况下,我们的接口逻辑都是串行化的,有时候在我们方法中可能存在着非常耗时的操作这样就会造成代码阻塞,但是呢,为了用户的体验,我们可能需要将一些复杂的数据开启线程进行异步处理。

所谓异步,其实就是实现一个可无需等待被调用函数的返回值而让操作继续运行的方法,简单的讲就是另启一个线程来完成调用中的部分计算,使调用继续运行或返回,而不需要等待计算结果。

Java8 提供的CompletableFuture 可以自定义线程池或使用默认线程池对数据进行异步处理,且可以根据需求选择是否返回异步结果!

一、runAsync 

1、runAsync(Runnable)

使用ForkJoinPool.commonPool()作为它的线程池执行异步代码

public class AsyncDemo01 {
    public static void main(String[] args) {
        //当前调用者线程为:main
        System.out.println("当前调用者线程为:" + Thread.currentThread().getName());
        CompletableFuture.runAsync(() -> {
            // 异步方法内当前执行线程为:ForkJoinPool.commonPool-worker-1
            System.out.println("异步方法内当前执行线程为:" + Thread.currentThread().getName());
            System.out.println(111);
        });
    }
}
2、runAsync(Runnable, Executor) 

使用指定的线程池执行异步代码。此异步方法无法返回值。

public class AsyncDemo02 {
    public static void main(String[] args) {
        //当前调用者线程为:main
        System.out.println("当前调用者线程为:" + Thread.currentThread().getName());
        
        // fixme 根据阿里规约 建议真实开发时使用 ThreadPoolExecutor 定义线程池
        ExecutorService threadPool = Executors.newFixedThreadPool(10);
        
        CompletableFuture.runAsync(() -> {
            // 异步方法内当前执行线程为:pool-1-thread-1
            System.out.println("异步方法内当前执行线程为:" + Thread.currentThread().getName());
            System.out.println(111);
        }, threadPool);
        
        // 演示代码,所以选择执行完后关闭线程池
        threadPool.shutdown();
    }
}

二、supplyAsync

1、supplyAsync(Supplier)

使用ForkJoinPool.commonPool()作为它的线程池执行异步代码,异步操作有返回值

public class SupplyDemo01 {
    public static void main(String[] args) {
        CompletableFuture<String> supplyAsync = CompletableFuture.supplyAsync(() -> {
            // 异步方法内当前执行线程为:ForkJoinPool.commonPool-worker-1
            System.out.println("异步方法内当前执行线程为:" + Thread.currentThread().getName());
            // 模拟返回值
            return "hello,world";
        });
        // 获取异步线程执行结果
        System.out.println(supplyAsync.join());
    }
}
2、supplyAsync(Supplier , Executor)

使用指定线程池 来执行可获取返回值的异步任务

public class SupplyDemo02 {
    public static void main(String[] args)  {
        // fixme 根据阿里规约 建议真实开发时使用 ThreadPoolExecutor 定义线程池
        ExecutorService threadPool = Executors.newFixedThreadPool(10);
        CompletableFuture<String> supplyAsync = CompletableFuture.supplyAsync(() -> {
            // 异步方法内当前执行线程为:pool-1-thread-1
            System.out.println("异步方法内当前执行线程为:" + Thread.currentThread().getName());
            // 模拟耗时与返回结果
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return "hello,world";
        },threadPool);
        // 获取异步线程执行结果
        System.out.println(supplyAsync.join());
    }
}

三、CompletableFuture中 get 与 join的区别

CompletableFuture 使用 supplyAsync 来执行异步任务的话,可通过调用 get 或 join方法便可获取异步线程的执行结果。

不同:get方法返回结果,抛出的是检查异常,必须用户throw或者try/catch处理,join返回结果,抛出未检查异常。

相同:join和get方法都是依赖于完成信号并返回结果T的阻塞方法。(阻塞调用者线程,等待异步线程返回结果)。 

join: 

get: 

需要注意的是, completableFuture 的get 方法 有重载,还有一个可传入获取结果等待时间的get方法

如果超过等待时间,异步线程还未返回结果,那么get 调用者线程则会抛出TimeoutException 异常 

 

四、thenApply方法 

当我们第二个任务依赖第一个任务的结果的时候,可以使用 thenApply相关方法来把这两个线程串行化,参数是一个Function(代表着我们需要传入一个转换的函数 具体可参考JAVA8 函数式接口)thenApply 只可以执行正常的任务,任务出现异常则不会执行 thenApply 方法;如果当第一个任务出现异常时仍要执行第二个任务,可以使用下方的Handle方法

public <U> CompletableFuture<U> thenApply(Function<? super T,? extends U> fn)
public <U> CompletableFuture<U> thenApplyAsync(Function<? super T,? extends U> fn)
public <U> CompletableFuture<U> thenApplyAsync(Function<? super T,? extends U> fn, Executor executor)

T:上一个异步任务返回值

U: 当前执行最后返回值

1、thenApply(Function) 

thenApply:thenApply中的线程为调用者线程,与CompletableFuture 底层默认所用的 ForkJoin无关!

示例:

public class SupplyDemo03 {
    public static void main(String[] args) {
        CompletableFuture<String> future = CompletableFuture
                .supplyAsync(() -> {
                    System.out.println(Thread.currentThread().getName());
                    return "hello";
                }).thenApply(e -> {
                    System.out.println(Thread.currentThread().getName());
                    return e + ",";
                }).thenApply(e -> {
                    System.out.println(Thread.currentThread().getName());
                    return (e + "world").toUpperCase();
                });
        System.out.println(future.join());
    }
}

程序执行情况:

最后JOIN 获取结果为 HELLO,WORLD

三个线程分别是 ForkJoinPool.commonPool-worker-1,main,main(2)thenApplyAsync(Function)
thenApplyAsync:当我们第二个任务依赖第一个任务的结果的时候,且第二个任务也想采用异步的方式,则可以使用 thenApplyAsync(Function)

public static void main(String[] args) {
    CompletableFuture<String> future = CompletableFuture
        .supplyAsync(() -> {
            System.out.println(Thread.currentThread().getName());
            return "hello";
        }).thenApplyAsync(e -> {
        System.out.println(Thread.currentThread().getName());
        return e + ",";
    }).thenApplyAsync(e -> {
        System.out.println(Thread.currentThread().getName());
        return (e + "world").toUpperCase();
    });
    System.out.println(future.join());
}

程序执行情况:

最后JOIN 获取结果为 HELLO,WORLD

三个线程均是 是 ForkJoinPool.commonPool-worker-xxxx

当然,我们也可以使用 thenApplyAsync(Function, Executor) 来使用自定义线程池执行我们的异步任务

五、handle方法 

handle 是执行任务完成时对结果的处理, handle 方法和 thenApply 方法处理方式大致一样,不同的是 handle 是在任务完成后再执行且Handle可以根据可以根据任务是否有异常来进行做相应的后续处理操作。

public <U> CompletionStage<U> handle(BiFunction<? super T, Throwable, ? extends U> fn);
public <U> CompletionStage<U> handleAsync(BiFunction<? super T, Throwable, ? extends U> fn);
public <U> CompletionStage<U> handleAsync(BiFunction<? super T, Throwable, ? extends U> fn,Executor executor);
 1、handle(BiFunction<T, Throwable, U> fn)

使用Handler方法 预估异常情况 进行逻辑处理 默认handle中使用的线程为调用者线程

public class HandleDemo01 {
    public static void main(String[] args) {
        CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
            System.out.println("当前supplyAsync 执行线程:" + Thread.currentThread().getName());
            // 模拟异常
            int a = 1 / 0;
            return "hello";
        }).handle((x, t) -> {
            System.out.println("当前handle 执行线程:" + Thread.currentThread().getName());
            if (t != null) {
                // 出现异常 打印异常信息 或者doSomething
                System.out.println("发现上一个异步任务出异常了" + t.getMessage());
            } else {
                // 未出异常 doSomething
                return x;
            }
            // 设置默认结果
            return "error";
        });
        System.out.println(future.join());
    }
}

程序执行情况:

当前supplyAsync 执行线程:ForkJoinPool.commonPool-worker-1
当前handle 执行线程:main
发现上一个异步任务出异常了java.lang.ArithmeticException: / by zero
error

2、handleAsync(BiFunction<T, Throwable, U> fn,Executor) 

使用自定义的线程 (或者使用默认的ForkJoin)进行异步处理第二个线程的任务 

public class HandleDemo01 {
    public static void main(String[] args) {
        ExecutorService threadPool = Executors.newFixedThreadPool(2);
        CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
            System.out.println("当前supplyAsync 执行线程:" + Thread.currentThread().getName());
            // 模拟异常
            int a = 1 / 0;
            return "hello";
        },threadPool).handleAsync((x, t) -> {
            System.out.println("当前handle 执行线程:" + Thread.currentThread().getName());
            if (t != null) {
                // 出现异常 打印异常信息 或者doSomething
                System.out.println("发现上一个异步任务出异常了" + t.getMessage());
            } else {
                // 未出异常 doSomething
                return x;
            }
            // 设置默认结果
            return "error";
        },threadPool);
        System.out.println(future.join());
    }
}

 程序执行情况:

当前supplyAsync 执行线程:pool-1-thread-1
当前handle 执行线程:pool-1-thread-2
发现上一个异步任务出异常了java.lang.ArithmeticException: / by zero
error

 六、thenCombine方法

thenCombine 会在两个CompletableFuture任务都执行完成后,把两个任务的结果一块处理。

public <U,V> CompletionStage<V> thenCombine(CompletionStage<? extends U> other,BiFunction<? super T,? super U,? extends V> fn);
public <U,V> CompletionStage<V> thenCombineAsync(CompletionStage<? extends U> other,BiFunction<? super T,? super U,? extends V> fn);
public <U,V> CompletionStage<V> thenCombineAsync(CompletionStage<? extends U> other,BiFunction<? super T,? super U,? extends V> fn,Executor executor);
1、thenCombine

thenCombine 会在两个CompletableFuture任务都执行完成后,调用者线程会把两个异步任务的结果一块处理

public class ThenCombineDemo01 {
    public static void main(String[] args) {
        CompletableFuture<String> helloAsync = CompletableFuture.supplyAsync(() -> {
            System.out.println("hello 执行线程:" + Thread.currentThread().getName());
            return "hello";
        });
        CompletableFuture<String> worldAsync = CompletableFuture.supplyAsync(() -> {
            System.out.println("world 执行线程:" + Thread.currentThread().getName());
            return "world";
        });
        CompletableFuture<String> result = worldAsync.thenCombine(helloAsync, (hello, world) -> {
            System.out.println("result 执行线程:" + Thread.currentThread().getName());
            return (hello + "," + world).toUpperCase();
        });
        System.out.println("获取结果 执行线程:" + Thread.currentThread().getName());
        System.out.println("两个异步任务合并结果:" + result.join());
    }
}

 程序执行结果:

hello 执行线程:ForkJoinPool.commonPool-worker-1
world 执行线程:ForkJoinPool.commonPool-worker-1
result 执行线程:main
获取结果 执行线程:main
两个异步任务合并结果:WORLD,HELLO

2、thenCombineAsync 

thenCombineAsync 会在两个CompletableFuture任务都执行完成后,再用一个异步线程把两个任务的结果一块处理

七、thenCompose方法

thenCompose 方法允许你对两个CompletableFuture任务进行流水线操作,当第一个异步任务操作完成时,会将其结果作为参数传递给第二个任务。

public <U> CompletableFuture<U> thenCompose(Function<? super T, ? extends CompletionStage<U>> fn);
public <U> CompletableFuture<U> thenComposeAsync(Function<? super T, ? extends CompletionStage<U>> fn) ;
public <U> CompletableFuture<U> thenComposeAsync(Function<? super T, ? extends CompletionStage<U>> fn, Executor executor) ;
1、thenCompose

thenCompose当第一个异步任务操作完成时,会将其结果作为参数传递给第二个任务(第二个任务为串行化操作,由调用者线程执行)

public class ThenComposeDemo01 {
    public static void main(String[] args) {
        CompletableFuture<String> result = CompletableFuture.supplyAsync(() -> {
            System.out.println("hello 执行线程:" + Thread.currentThread().getName());
            return "hello";
        }).thenCompose((hello -> {
            System.out.println("thenCompose 执行线程:" + Thread.currentThread().getName());
            return CompletableFuture.supplyAsync((hello + "world")::toUpperCase);
        }));
        System.out.println("获取结果 执行线程:" + Thread.currentThread().getName());
        System.out.println("两个异步任务流水线执行结果:" + result.join());
    }
}

程序执行情况:

hello 执行线程:ForkJoinPool.commonPool-worker-1
thenCompose 执行线程:main
获取结果 执行线程:main
两个异步任务流水线执行结果: HELLOWORLD(2)thenComposeAsync

thenComposeAsync当第一个异步任务操作完成时,会将其结果作为参数传递给第二个任务(第二个任务仍为异步线程执行操作,可由默认ForkJoin线程池执行,也可使用自定义线程池)

public class ThenComposeDemo02 {
    public static void main(String[] args) {
        ExecutorService threadPool = Executors.newFixedThreadPool(2);
        CompletableFuture<String> result = CompletableFuture.supplyAsync(() -> {
            System.out.println("hello 执行线程:" + Thread.currentThread().getName());
            return "hello";
        },threadPool).thenComposeAsync((hello -> {
            System.out.println("thenCompose 执行线程:" + Thread.currentThread().getName());
            return CompletableFuture.supplyAsync((hello + "world")::toUpperCase);
        }),threadPool);
        System.out.println("获取结果 执行线程:" + Thread.currentThread().getName());
        System.out.println("两个异步任务流水线执行结果:" + result.join());
    }
}

程序执行情况:

hello 执行线程:pool-1-thread-1
获取结果 执行线程:main
thenCompose 执行线程:pool-1-thread-2
两个异步任务流水线执行结果: HELLOWORLD

八、allOf / anyOf

allOf:CompletableFuture是多个任务都执行完成后才会执行,只有有一个任务执行异常,则返回的CompletableFuture执行get方法时会抛出异常,如果都是正常执行,则get返回null。

anyOf :CompletableFuture是多个任务只要有一个任务执行完成,则返回的CompletableFuture执行get方法时会抛出异常,如果都是正常执行,则get返回执行完成任务的结果。

测试代码:

public static void main(String[] args) throws ExecutionException, InterruptedException {
        CompletableFuture<String> cf1 = CompletableFuture.supplyAsync(() -> {
            try {
                System.out.println(Thread.currentThread() + " cf1 do something....");
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("cf1 任务完成");
            return "cf1 任务完成";
        });
 
        CompletableFuture<String> cf2 = CompletableFuture.supplyAsync(() -> {
            try {
                System.out.println(Thread.currentThread() + " cf2 do something....");
                int a = 1/0;
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("cf2 任务完成");
            return "cf2 任务完成";
        });
 
        CompletableFuture<String> cf3 = CompletableFuture.supplyAsync(() -> {
            try {
                System.out.println(Thread.currentThread() + " cf2 do something....");
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("cf3 任务完成");
            return "cf3 任务完成";
        });
 
        CompletableFuture<Void> cfAll = CompletableFuture.allOf(cf1, cf2, cf3);
        System.out.println("cfAll结果->" + cfAll.get());
}
 
 
public static void main(String[] args) throws ExecutionException, InterruptedException {
        CompletableFuture<String> cf1 = CompletableFuture.supplyAsync(() -> {
            try {
                System.out.println(Thread.currentThread() + " cf1 do something....");
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("cf1 任务完成");
            return "cf1 任务完成";
        });
 
        CompletableFuture<String> cf2 = CompletableFuture.supplyAsync(() -> {
            try {
                System.out.println(Thread.currentThread() + " cf2 do something....");
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("cf2 任务完成");
            return "cf2 任务完成";
        });
 
        CompletableFuture<String> cf3 = CompletableFuture.supplyAsync(() -> {
            try {
                System.out.println(Thread.currentThread() + " cf2 do something....");
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("cf3 任务完成");
            return "cf3 任务完成";
        });
 
        CompletableFuture<Object> cfAll = CompletableFuture.anyOf(cf1, cf2, cf3);
        System.out.println("cfAll结果->" + cfAll.get());
}

<think>嗯,用户想了解CompletableFuture的原理和详细实现,特别是Java异步编程方面的深入分析。首先,我需要回顾一下之前提供的引用内容,看看有哪些信息可以利用。引用[1]提到CompletableFutureJava 8引入的,用于异步编程,属于java.util.concurrent包,相比传统Future更灵活,支持链式调用、任务合并和异常处理。引用[2]强调了它的高效异步模式和并发编程的简洁性。引用[3]指出CompletableFuture有很多方法和内部类,需要分步分析。引用[4]提到了Java并发的发展,包括ForkJoin框架等。 用户的问题是关于原理和实现的详细解析,所以需要深入讨论内部机制,比如如何实现异步任务的管理、回调链的处理、线程池的使用,以及如何组合多个任务。可能还需要涉及CompletableFuture的状态机、依赖关系处理、异常传播机制等。 首先,CompletableFuture基于ForkJoinPool,这点应该在实现原理部分提到。然后,它的核心结构可能涉及到每个阶段(stage)作为一个节点,形成链表或栈结构来处理依赖。状态管理方面,可能用volatile变量来保存结果和状态变化,确保可见性。 另外,需要解释CompletableFuture如何通过thenApply、thenAccept等方法链式组合任务,这些方法如何注册回调,并在前一个阶段完成时触发后续操作。这里可能涉及CompletionStage接口的实现,以及各种不同类型的Completion子类,比如UniApply、BiApply等,处理不同的依赖情况。 异常处理也是一个重点,CompletableFuture如何捕获异常并传递给后续的exceptionally或handle方法。需要说明当某个阶段出现异常时,后续依赖的阶段如何被触发,并处理异常结果。 线程池的集成部分,用户可能想知道默认使用的线程池,以及如何自定义。比如,默认使用ForkJoinPool.commonPool(),但也可以指定自定义的Executor。在实现中,如何将任务提交到线程池,并在任务完成后执行回调。 还有,组合多个CompletableFuture的方法,如allOf、anyOf,这些是如何实现的,可能涉及内部计数器或条件判断,等待所有或任意一个Future完成。 另外,性能优化方面,CompletableFuture如何减少线程阻塞,通过非阻塞的回调机制提高效率。对比传统的Future.get(),CompletableFuture通过回调通知机制避免了阻塞等待。 可能用户还想了解与响应式编程的区别,或者与其他异步框架如RxJava的不同之处,但根据问题,重点应放在CompletableFuture本身的原理和实现细节上。 需要确保回答结构清晰,分点讲解核心原理、状态管理、依赖处理、线程池机制、组合任务和异常处理等部分。同时,结合代码示例说明,比如创建异步任务的方法,链式调用的例子,以及异常处理的示例。 最后,生成相关问题时要围绕用户可能的进一步疑问,比如与传统的Future对比、如何选择线程池、实际应用场景、性能优化方法、异常处理最佳实践,以及和响应式编程的结合等。</think># CompletableFuture原理与实现深度解析 ## 一、核心实现原理 CompletableFuture基于观察者模式构建异步任务链,内部采用$stage$链表结构管理任务依赖关系。每个阶段通过$UniCompletion$/$BiCompletion$等内部类实现回调注册,采用无锁CAS操作保证线程安全[^3]。 状态机实现包含三个关键变量: $$ \begin{cases} result & \text{存储计算结果/异常对象} \\ stack & \text{等待触发的回调函数栈} \\ nest & \text{防止递归调用计数器} \end{cases} $$ ## 二、任务执行流程 1. **异步启动**:通过`supplyAsync()`创建任务时,默认使用$ForkJoinPool.commonPool()$[^4] ```java CompletableFuture<String> cf = CompletableFuture.supplyAsync(() -> { // 异步任务逻辑 return "result"; }); ``` 2. **依赖传播**:每个`thenApply()`调用生成新的$UniApply$节点并压入前驱的栈顶 3. **完成触发**:当前任务完成后,通过`postComplete()`方法反向遍历栈节点触发后续操作 ## 三、线程池集成机制 | 配置方式 | 实现类 | 适用场景 | |-------------------|-------------------------|-----------------------| | 默认线程池 | ForkJoinPool.commonPool | CPU密集型任务 | | 自定义线程池 | ThreadPoolExecutor | IO密集型任务 | | 直接执行 | Runnable::run | 快速回调(同线程执行) | ## 四、组合任务实现 1. **AND聚合**:`allOf()`通过$BiRelay$合并多个future结果 2. **OR聚合**:`anyOf()`使用$AtomicReference$竞争设置首个完成结果 3. **嵌套组合**:`thenCompose()`通过$UniCompose$实现future级联 ## 五、异常处理机制 1. 异常传播路径:通过$AltResult$包装异常对象沿任务链传递 2. 恢复机制:`exceptionally()`注册的恢复函数可返回替代值 3. 全局捕获:`handle()`方法同时接收结果和异常参数 ```java CompletableFuture.supplyAsync(() -> { if (new Random().nextBoolean()) throw new RuntimeException("模拟异常"); return "data"; }).exceptionally(ex -> { System.out.println("捕获异常: " + ex.getMessage()); return "default"; }); ``` ## 六、性能优化设计 1. 延迟提交:使用$LazyExecutor$减少线程切换开销 2. 栈优化:采用Treiber栈结构提升并发压栈性能[^2] 3. 对象复用:内部$ASYNC$/$NIL$等标记对象减少内存分配
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值