zoukankan      html  css  js  c++  java
  • CompletableFuture详解

    在JDK1.5已经提供了Future和Callable的实现,可以用于阻塞式获取结果,如果想要异步获取结果,通常都会以轮询的方式去获取结果,如下:

    //定义一个异步任务
    Future<String> future = executor.submit(()->{
           Thread.sleep(2000);
           return "hello world";
    });
    //轮询获取结果
    while (true){
        if(future.isDone()) {
             System.out.println(future.get());
             break;
         }
     }
    

      从上面的形式看来轮询的方式会耗费无谓的CPU资源,而且也不能及时地得到计算结果.所以要实现真正的异步,上述这样是完全不够的,在Netty中,我们随处可见异步编程

    而JDK1.8中的CompletableFuture就为我们提供了异步函数式编程,CompletableFuture提供了非常强大的Future的扩展功能,可以帮助我们简化异步编程的复杂性,提供了函数式编程的能力,可以通过回调的方式处理计算结果,并且提供了转换和组合CompletableFuture的方法。

    1. 创建CompletableFuture对象

        CompletableFuture提供了四个静态方法用来创建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)
    

      supplyAsyncrunAsync不同在与前者异步返回一个结果,后者是void;

      supply是一个函数值接口

    2. 主动计算

        以下4个方法用于获取结果

    //同步获取结果
    public T    get()
    public T    get(long timeout, TimeUnit unit)
    public T    getNow(T valueIfAbsent)
    public T    join()
    

      

    getNow有点特殊,如果结果已经计算完则返回结果或者抛出异常,否则返回给定的valueIfAbsent值。join()get()区别在于join()返回计算的结果或者抛出一个unchecked异常(CompletionException),而get()返回一个具体的异常.

    • 主动触发计算.
    public boolean complete(T  value)
    public boolean completeExceptionally(Throwable ex)
    

    上面方法表示当调用CompletableFuture.get()被阻塞的时候,那么这个方法就是结束阻塞,并且get()获取设置的value.

     public static CompletableFuture<Integer> compute() {
            final CompletableFuture<Integer> future = new CompletableFuture<>();
            return future;
        }
        public static void main(String[] args) throws Exception {
            final CompletableFuture<Integer> f = compute();
            class Client extends Thread {
                CompletableFuture<Integer> f;
                Client(String threadName, CompletableFuture<Integer> f) {
                    super(threadName);
                    this.f = f;
                }
                @Override
                public void run() {
                    try {
                        System.out.println(this.getName() + ": " + f.get());
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    } catch (ExecutionException e) {
                        e.printStackTrace();
                    }
                }
            }
            new Client("Client1", f).start();
            new Client("Client2", f).start();
            System.out.println("waiting");
            //设置Future.get()获取到的值
            f.complete(100);
            //以异常的形式触发计算
            //f.completeExceptionally(new Exception());
            Thread.sleep(1000);
        }
    

      

    3. 计算结果完成时的处理

    public CompletableFuture<T>     whenComplete(BiConsumer<? super T,? super Throwable> action)
    public CompletableFuture<T>     whenCompleteAsync(BiConsumer<? super T,? super Throwable> action)
    public CompletableFuture<T>     whenCompleteAsync(BiConsumer<? super T,? super Throwable> action, Executor executor)
    public CompletableFuture<T>     exceptionally(Function<Throwable,? extends T> fn)
    

    上面4个方法是当计算阶段结束的时候触发,BiConsumer有两个入参,分别代表计算返回值,另外一个是异常.无返回值.方法不以Async结尾,意味着Action使用相同的线程执行,而Async可能会使用其它的线程去执行(如果使用相同的线程池,也可能会被同一个线程选中执行)。

    future.whenCompleteAsync((v,e)->{
           System.out.println("return value:"+v+"  exception:"+e);
     });
    
    • handle()
    public <U> CompletableFuture<U>     handle(BiFunction<? super T,Throwable,? extends U> fn)
    public <U> CompletableFuture<U>     handleAsync(BiFunction<? super T,Throwable,? extends U> fn)
    public <U> CompletableFuture<U>     handleAsync(BiFunction<? super T,Throwable,? extends U> fn, Executor executor)
    

    whenComplete()不同的是这个函数返回CompletableFuture并不是原始的CompletableFuture返回的值,而是BiFunction返回的值.

    4. CompletableFuture的组合

    • thenApply
      当计算结算完成之后,后面可以接继续一系列的thenApply,来完成值的转化.
    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)
    

    它们与handle方法的区别在于handle方法会处理正常计算值和异常,因此它可以屏蔽异常,避免异常继续抛出。而thenApply方法只是用来处理正常值,因此一旦有异常就会抛出。

      CompletableFuture<String> future = CompletableFuture.supplyAsync(()->{
                
                return "hello world";
            });
    
    CompletableFuture<String> future3 = future.thenApply((element)->{
                return element+"  addPart";
            }).thenApply((element)->{
                return element+"  addTwoPart";
            });
            System.out.println(future3.get());//hello world  addPart  addTwoPart
    

    5. CompletableFuture的Consumer

    只对CompletableFuture的结果进行消费,无返回值,也就是最后的CompletableFuture是void.

    public CompletableFuture<Void>  thenAccept(Consumer<? super T> action)
    public CompletableFuture<Void>  thenAcceptAsync(Consumer<? super T> action)
    public CompletableFuture<Void>  thenAcceptAsync(Consumer<? super T> action, Executor executor)
    
    //入参为原始的CompletableFuture的结果.
    CompletableFuture future4 = future.thenAccept((e)->{
                System.out.println("without return value");
            });
    future4.get();
    
    • thenAcceptBoth

    这个方法用来组合两个CompletableFuture,其中一个CompletableFuture等待另一个CompletableFuture的结果.

    CompletableFuture<String> future = CompletableFuture.supplyAsync(()->{
                return "hello world";
            });
    CompletableFuture future5 =  future.thenAcceptBoth(CompletableFuture.completedFuture("compose"),
                    (x, y) -> System.out.println(x+y));//hello world compose
    

    6. Either和ALL

        thenAcceptBoth是当两个CompletableFuture都计算完成,而我们下面要了解的方法applyToEither是当任意一个CompletableFuture计算完成的时候就会执行。

    Random rand = new Random();
            CompletableFuture<Integer> future9 = CompletableFuture.supplyAsync(() -> {
                try {
                    Thread.sleep(1000 + rand.nextInt(1000));
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                return 100;
            });
            CompletableFuture<Integer> future10 = CompletableFuture.supplyAsync(() -> {
                try {
                    Thread.sleep(1000 + rand.nextInt(1000));
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                return 200;
            });
            //两个中任意一个计算完成,那么触发Runnable的执行
            CompletableFuture<String> f =  future10.applyToEither(future9,i -> i.toString());
            //两个都计算完成,那么触发Runnable的执行
            CompletableFuture f1 = future10.acceptEither(future9,(e)->{
                System.out.println(e);
            });
            System.out.println(f.get());
    

        如果想组合超过2个以上的CompletableFuture,allOfanyOf可能会满足你的要求.allOf方法是当所有的CompletableFuture都执行完后执行计算。anyOf方法是当任意一个CompletableFuture执行完后就会执行计算,计算的结果相同。

    链接:https://www.jianshu.com/p/547d2d7761db

    使用示例

    https://developer.ibm.com/zh/articles/j-cf-of-jdk8/

  • 相关阅读:
    C#获取MAC地址
    C#洗牌
    删除前提示
    简单MD5加密
    读取EXCEL所有列名C#
    gridview二次加载样式丢失
    迅雷API接口(及示例演示)
    [转]Servlet过滤器介绍之原理分析
    [转]MySQL运行状态show status中文详解
    [转]如何修改mysql root密码
  • 原文地址:https://www.cnblogs.com/anhaogoon/p/13492789.html
Copyright © 2011-2022 走看看