JAVA8新特性CompletableFuture

CompletableFuture比Future好在哪

future接口可以构建异步应用,但依然有其局限性。它很难直接表述多个Future 结果之间的依赖性。实际开发中,我们经常需要达成以下目的:

  • 将两个异步计算合并为一个——这两个异步计算之间相互独立,同时第二个又依赖于第
    一个的结果。
  • 等待 Future 集合中的所有任务都完成。
  • 仅等待 Future 集合中最快结束的任务完成(有可能因为它们试图通过不同的方式计算同
    一个值),并返回它的结果。
  • 通过编程方式完成一个 Future 任务的执行(即以手工设定异步操作结果的方式)。
  • 应对 Future 的完成事件(即当 Future 的完成事件发生时会收到通知,并能使用 Future
    计算的结果进行下一步的操作,不只是简单地阻塞等待操作的结果)

新的CompletableFuture将使得这些成为可能。

备注:所有以Async结尾的方法都表明该方法可以异步执行,默认在ForkJoinPool.commonPool中执行,你也可以传入一个Executor 规定方法在指定线程池中执行

CompletableFuture的静态工厂方法

方法

返回值

描述

runAsync(Runnable runnable)

CompletableFuture

执行一个异步线程获取CompletableFuture,里面没有返回值,在默认线程池中执行

runAsync(Runnable runnable, Executor executor)

CompletableFuture

执行一个异步线程获取CompletableFuture,里面没有返回值,在指定线程池中执行

supplyAsync(Supplier supplier)

CompletableFuture

传入一个lambda表达式获取一个CompletableFuture,有返回值在默认线程池中执行

supplyAsync(Supplier supplier, Executor executor)

CompletableFuture

传入一个lambda表达式获取一个CompletableFuture,有返回值在指定线程池中执行

/**
 * @author 
 * @description CompletableFuture的静态工厂方法
    1.runAsync(Runnable runnable)
    2.runAsync(Runnable runnable, Executor executor)
    3.supplyAsync(Supplier<T> supplier)
    4.supplyAsync(Supplier<T> supplier, Executor executor)
 * @create 2018-09-05
 **/
public class Demo1 {

    public static ExecutorService myExecutorService = Executors.newCachedThreadPool();

    public static void main(String[] args) {
        //方式1 runAsync(Runnable runnable) 异步任务提交到默认线程池
        CompletableFuture future1 = CompletableFuture.runAsync(()->{
            delay(2);
            System.out.println("11");
        });
        try {
            //调用get以后会在2s后打印11
            future1.get();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }


        //方式2 runAsync(Runnable runnable, Excutor excutor) 将异步任务提交到指定线程池
        CompletableFuture future2 = CompletableFuture.runAsync(()->{
            delay(2);
            System.out.println("11");
        }, myExecutorService);
        try {
            //调用get以后会在2s后打印11
            future1.get();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }

        //使用runAsync方法是获取不到返回值得  如果你需要获取异步线程执行的结果,就需要使用supplyAsync

        //方式3 supplyAsync(Supplier<T> supplier) 异步任务提交到默认线程池
        CompletableFuture<String> future3 = CompletableFuture.supplyAsync(()->{
            delay(2);
            return "123";
        });
        try {
            //调用get后可以获取异步执行的结果使用
            String s = future3.get();
            System.out.println(s);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }

        //方式3 supplyAsync(Supplier<T> supplier) 异步任务提交到默认线程池
        CompletableFuture<String> future4 = CompletableFuture.supplyAsync(()->{
            delay(3);
            return "234";
        }, myExecutorService);
        try {
            //调用get后可以获取异步执行的结果使用
            String s = future4.get();
            System.out.println(s);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }

    }

    public static void delay(int a){

        try {
            Thread.sleep(a*1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

获取CompletableFuture结果的几种方式

  • 获取单个CompletableFuture的结果

方法

描述

get()

无线等待,需要捕获异常

get(long timeout, TimeUnit timeunit)

等待有限的时间,会抛出异常,超时会抛出TimeoutException

getNow(T)

立刻获取返回值,如果未完成返回提供的默认值 没有异常

join

无限等待没有抛出异常

  • 获取多个CompletableFuture的结果

方法

描述

allOf

等待所有CompletableFuture完成后获取结果,但是没有返回值

anyOf

只要有一个结果返回就可以获取值,有返回值

/** 
 * @author 
 * @description 获取结果的几种方式比较 
 1. get() 无线等待,需要捕获异常 
 2. get(long timeout, TimeUnit timeunit) 等待有限的时间,会抛出异常 
 3. getNow(T) 立刻获取返回值,如果未完成返回提供的默认值 没有异常 
 4. T join; 无限等待没有异常 
 多个CompletableFuture结果的获取策略 
 1.allOf 等待所有结果 
 2.anyOf 只要有一个结果返回就可以获取值 
 * @create 2018-09-05 
 **/ 
 public class Demo2 {public static void main(String[] args) {

      CompletableFuture<String> future = CompletableFuture.supplyAsync(()->{
          delay(2);
          return "123";
      });

      //1. get()  无线等待,需要捕获异常
      try {
          System.out.println(future.get());
      } catch (InterruptedException e) {
          e.printStackTrace();
      } catch (ExecutionException e) {
          e.printStackTrace();
      }

      //2. get(long timeout, TimeUnit timeunit)  等待有限的时间,会抛出异常
      CompletableFuture<String> future2 = CompletableFuture.supplyAsync(()->{
          delay(2);
          return "123";
      });
      try {
          System.out.println(future2.get(1, TimeUnit.SECONDS));
      } catch (InterruptedException e) {
          e.printStackTrace();
      } catch (ExecutionException e) {
          e.printStackTrace();
      } catch (TimeoutException e) {
          System.out.println("还没完成就获取  抛出TimeoutException");
      }

      //3. getNow(T) 立刻获取返回值,如果未完成返回提供的默认值 没有异常
      CompletableFuture<String> future3 = CompletableFuture.supplyAsync(()->{
          delay(2);
          return "123";
      });
          System.out.println("future3------"+future3.getNow("default"));


      //4. T join;      无限等待没有异常
      CompletableFuture<String> future4 = CompletableFuture.supplyAsync(()->{
          delay(2);
          return "123";
      });
      System.out.println("future4------"+future4.join());

      //异常处理
      CompletableFuture<String> future5 = new CompletableFuture<>();
      new Thread(()->{
          delay(2);
          try {
              throw new Exception("error  le");
          } catch (Exception e) {
              future5.completeExceptionally(e);
          }
      }).start();
      try {
          future5.get();
      } catch (InterruptedException e) {
          e.printStackTrace();
      } catch (ExecutionException e) {
          System.out.println("future5------get dao 异常了"+e);
      }

      //多个CompletableFuture的获取策略allOf, anyof
      /**
       * 模拟场景:获取2种商品的价格,然后获取总额
       */

      //由于allof没有返回值,所以需要将allprice传入,这里为了避免线程问题用AtomicInteger
      AtomicInteger allPrice = new AtomicInteger();

      List<String> ids = Arrays.asList("1","2");
      System.out.println("+++++");
      CompletableFuture<Void> res = CompletableFuture.allOf(ids.stream().map(
              id -> {
                  return CompletableFuture.supplyAsync(() -> {
                      return allPrice.addAndGet(getPriceById(id));
                  });
              }
      ).toArray(CompletableFuture[]::new));
      System.out.println("do something else--");
      res.join();
      System.out.println("_______"+allPrice);


      /**
       * 模拟场景:同一个产品,有两种方式获取价格的方式的,谁先返回就返回哪个结果
       *
       */

      CompletableFuture res2 = CompletableFuture.anyOf(ids.stream().map(id->{
          return CompletableFuture.supplyAsync(()->{
              return getPriceById(id);
          });
      }).toArray(CompletableFuture[]::new));
      System.out.println("do something else 2---------");

      try {
          //输入的是delay时间较短的结果
          System.out.println(res2.get());
      } catch (Exception e) {
          e.printStackTrace();
      }

  }

  public static int getPriceById(String id){
      int a = new Random().nextInt(5) ;
      System.out.println("id:"+id +"____delay:"+a);
      delay(a);
      return a ;
  }



  public static void delay(int a){

      try {
          Thread.sleep(a*1000);
      } catch (InterruptedException e) {
          e.printStackTrace();
      }
  }}

如何编排你的异步任务

下面每种方法都有对应的异步执行方法和指定线程池的方法,至于什么时候用同步什么时候用异步取决你里面执行逻辑的耗时,耗时短可以选择同步,如果耗时比较多应该使用异步

方法

描述

thenCompose

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

thenCombine

两个future都完成时定义处理逻辑

thenAccept

消费结果,不返回

thenApply

结果转换

/**
 * @author
 * @description  处理多个CompletableFuture的方法
 *               thenCompose  方法允许你对两个异步操作进行流水线,第一个操作完成时,将其结果作为参数传递给第二个操作。
 *               thenCombine  两个future都完成时定义处理逻辑
 *               thenAccept   消费结果,不返回
 *               thenApply    将前面一部的结果进行转换
 * @create 2018-09-06
 **/
public class Demo3 {

    public static void main(String[] args) {


        /**
         * 模拟调用2个服务,第一个服务返回hello,第二个返回world,最后返回hello world
         */
        long t1 = System.currentTimeMillis();
        CompletableFuture<String> allRes = CompletableFuture.supplyAsync(()->{
            delay(1);
            return "hello";
        }).thenComposeAsync((res)->{
            return CompletableFuture.supplyAsync(()->{
                delay(2);
                return res + " world ";
            });
        });
        System.out.println("do something-----");

        System.out.println("--res:"+allRes.join());
        long t2 = System.currentTimeMillis();
        long cost = t2 - t1 ;
        System.out.println("cost : "+cost);

        /**
         * 模拟同时调用2个服务,第一个服务返回hello,第二个返回world,最后返回hello world,节省耗时
         */

        long t3 = System.currentTimeMillis();
        CompletableFuture<String> allRes2 = CompletableFuture.supplyAsync(()->{
            delay(1);
            return "hello";
        }).thenCombine(CompletableFuture.supplyAsync(()->{
            delay(2);
            return "world";
        }), (res1,res2)->{
            return res1+" "+res2 ;
        }).thenCombine(CompletableFuture.supplyAsync(()->{
            delay(3);
            return "Tmac";
                }),(res3,res4)->{

            return res3+res4 ;
        });
        System.out.println("do something-----");

        System.out.println("--res2:"+allRes2.join());
        long t4 = System.currentTimeMillis();
        long cost2 = t4 - t3 ;
        //由于同时发请求,这里耗时是2s
        System.out.println("cost2 : "+cost2);


        /**
         * 拟调用2个服务,第一个服务返回hello,第二个返回world,最后只要打印出hello world就可以了
         */
        CompletableFuture res3 = CompletableFuture.supplyAsync(()->{
            delay(2);
            return "hello";
        }).thenAcceptAsync((res)->{
            System.out.println( res + " world ");
        });

        System.out.println("do something-----");
        res3.join();

        /**
         * 拟调用2个服务,第一个服务返回1,如果返回1调用服务二将其转换成hello world
         */
        CompletableFuture res4 = CompletableFuture.supplyAsync(()->{
            delay(2);
            return "1";
        }).thenApplyAsync((res)->{
            delay(1);
            String wrapper = "" ;
            if("1".equals(res)){
                wrapper = "hello world" ;
            }
            return wrapper;
        });

        System.out.println("do something-----");
        System.out.println(res4.join());

    }


    public static void delay(int a){

        try {
            Thread.sleep(a*1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}