JUC(二)CompletableFuture

201 阅读4分钟

CompletableFuture

CompletableFuture的简单介绍

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

  2. 它可能代表一个明确完成的Future,也有可能代表一个完成阶段(CompletionStages),它支持在计算完成以后触发一些函数或执行某些动作。

  3. 它实现了Future和CompletionStage接口

    image-20221215204350871

CompletableFuture的核心的四个静态方法

    // 创建一个异步操作 并且有返回值
    public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier) {
        return asyncSupplyStage(asyncPool, supplier);
    }
​
    // 创建一个异步操作 并且有返回值
    public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier,
                                                       Executor executor) {
        return asyncSupplyStage(screenExecutor(executor), supplier);
    }
​
    // 创建一个异步操作 无返回值
    public static CompletableFuture<Void> runAsync(Runnable runnable) {
        return asyncRunStage(asyncPool, runnable);
    }
​
    // 创建一个异步操作 无返回值
    public static CompletableFuture<Void> runAsync(Runnable runnable,
                                                   Executor executor) {
        return asyncRunStage(screenExecutor(executor), runnable);
    }
  1. 简单代码演示
package com.lichennan;
​
import java.util.concurrent.*;
​
public class CompletableFutureDemo {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        // 线程工厂
        ThreadFactory t1 = r -> new Thread(r,"demo1" + ThreadLocalRandom.current().nextInt(100));
        // 自定义线程池
        ThreadPoolExecutor threadPool = new ThreadPoolExecutor(12, 24, 30, TimeUnit.MILLISECONDS, new ArrayBlockingQueue<>(2000), t1);
​
        // 任务一
        CompletableFuture<Void> future = CompletableFuture.runAsync(() -> System.out.println(Thread.currentThread().getName() + "执行任务1"), threadPool);
        // 任务二
        CompletableFuture<Integer> future1 = CompletableFuture.supplyAsync(() -> {
            System.out.println(Thread.currentThread().getName() + "执行任务2");
            return 1;
        }, threadPool);
        
        System.out.println(future.get());
        System.out.println(future1.get());
​
    }
}
​

demo15执行任务1 null demo185执行任务2 1

函数式编程小总结

img

应用

  1. 需求:

    我们去比同一个商品在各个平台上的价格,要求获得一个清单列表,

    1 step by step,查完京东查淘宝,查完淘宝查天猫......

    2 all 一口气同时查询。。。。。

  2. 代码实现

    package com.lichennan;
    ​
    import java.util.ArrayList;
    import java.util.Arrays;
    import java.util.List;
    import java.util.concurrent.CompletableFuture;
    import java.util.concurrent.ThreadLocalRandom;
    import java.util.concurrent.TimeUnit;
    import java.util.stream.Collectors;
    ​
    public class NetMallDemo {
    ​
        /**
         * 电商网站
         */
        static List<NetMall> list = Arrays.asList(
                new NetMall("jd"),
                new NetMall("tb"),
                new NetMall("pdd")
        );
        public static void main(String[] args) {
            long l = System.currentTimeMillis();
            List<String> list1 = list(list, "Java并发编程的艺术");
            list1.forEach(System.out::println);
            System.out.println(System.currentTimeMillis()-l);
        }
    ​
    ​
        /**
         * 获取各电商网站商品价格
         *
         * @param list        列表
         * @param productName 产品名称
         * @return {@link List}<{@link String}>
         */
        public static List<String> list(List<NetMall> list, String productName){
           return list.stream()
                   .map(m->CompletableFuture.supplyAsync(()->String.format(productName + " %s price is %.2f", m.getNetMallName(), m.getPriceByName(productName))))
                   .collect(Collectors.toList()).stream().map(CompletableFuture::join).collect(Collectors.toList());
        }
    ​
    ​
    }
    ​
    /**
     * 电商网站
     * @author licn9901@163.com
     * @date 2022/12/15
     */
    class NetMall
    {
    ​
        private String netMallName;
    ​
        public NetMall(String netMallName)
        {
            this.netMallName = netMallName;
        }
    ​
        public double getPriceByName(String productName)
        {
            return calcPrice(productName);
        }
    ​
        private double calcPrice(String productName)
        {
            try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); }
            return ThreadLocalRandom.current().nextDouble() + productName.charAt(0);
        }
    ​
        public String getNetMallName() {
            return netMallName;
        }
    }
    

    Java并发编程的艺术 jd price is 74.21 Java并发编程的艺术 tb price is 74.30 Java并发编程的艺术 pdd price is 74.44 1085

CompletableFuture常用方法

  1. 获得结果和触发计算

    1. 获取结果

      public T get() // 不见不散 
      public T get(long timeout, TimeUnit unit) // 过时不候
      // 没有计算完成的情况下,有一个替代结果
      // 立即获取结果不阻塞 计算完,返回计算完成后的结果,没算完,返回设定的valueIfAbsent值   
      public T getNow(T valueIfAbsent) 
      public T join()    
      
    2. 主动触发计算

      public boolean complete(T value) // 是否打断get方法立即返回括号值
      
    3. 代码演示

          /**
           * 获得结果和触发计算
           */
          public static void test1() throws ExecutionException, InterruptedException, TimeoutException {
              // 不见不散
              System.out.println(CompletableFuture
                      .supplyAsync(() -> 1)
                      .get());
              System.out.println(CompletableFuture
                      .supplyAsync(() -> 1)
                      .join());
      ​
              // 过时不候
              System.out.println(CompletableFuture.supplyAsync(()->{
                  try {
                      TimeUnit.SECONDS.sleep(2);
                  } catch (InterruptedException e) {
                      throw new RuntimeException(e);
                  }
                  return 3;
              }).get(4,TimeUnit.SECONDS));
      ​
              // 立即获取结果不阻塞 计算完,返回计算完成后的结果,没算完,返回设定的valueIfAbsent值
              System.out.println(CompletableFuture.supplyAsync(() -> {
                  try {
                      TimeUnit.SECONDS.sleep(2);
                  } catch (InterruptedException e) {
                      throw new RuntimeException(e);
                  }
                  return 3;
              }).getNow(2));
              // 是否打断get方法立即返回括号值
              CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
                  try {
                      TimeUnit.SECONDS.sleep(2);
                  } catch (InterruptedException e) {
                      throw new RuntimeException(e);
                  }
                  return 3;
              });
              System.out.println(future.complete(4)+" "+future.get());
          }
      
  2. 对计算结果进行处理

    1. thanApply

      // 计算结果存在依赖关系,这两个线程串行化
      // 由于存在依赖关系(当前步错,不走下一步),当前步骤有异常的话就叫停。
      System.out.println(CompletableFuture
                .supplyAsync(() -> 1)
                .thenApply(f -> {
                  //int i = 1/0;
                  return f + 2;
              }).thenApply(f1->{
                  return f1 + 2;
              }).get());
      
    2. handle

      // 有异常也可以往下走一步,根据带的异常参数可以进一步处理
       // 有异常也可以往下走一步,根据带的异常参数可以进一步处理
      System.out.println(CompletableFuture.supplyAsync(() -> 1)
                .handle((f,e) -> {
                  //int i = 1/0;
                  return f + 2;
              }).handle((f1,e)-> f1 + 2).whenCompleteAsync((f, e)->{
                          System.out.println(f);
              }).exceptionally(e->{
                  e.printStackTrace();
                  return null;
              }).get());
      

      img

      img

  3. 对计算结果进行消费

    // 接收任务的处理结果,并消费处理,无返回结果
    // thenAccept
                CompletableFuture
                    .supplyAsync(() -> 1)
                    .thenApply(f -> f + 2)
                    .thenApply(f -> f + 3)
                    .thenApply(f -> f + 4)
                    .thenAccept(System.out::println);
    

    任务之间的顺序执行

    thenRun:任务A执行完执行B,并且B不需要A的结果

    thenAccept: 任务A执行完执行B,B需要A的结果,但是任务B无返回值

    thenApplay:任务A执行完毕执行B,B需要A的结果,同时任务B有返回值

  4. 对计算速度进行选用

    // 谁快用谁
    public static void test4() throws ExecutionException, InterruptedException {
            CompletableFuture<Integer> completableFuture1 = CompletableFuture.supplyAsync(() -> {
                System.out.println(Thread.currentThread().getName() + "\t" + "---come in ");
                //暂停几秒钟线程
                try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); }
                return 10;
            });
    ​
            CompletableFuture completableFuture2 = CompletableFuture.supplyAsync(() -> {
                System.out.println(Thread.currentThread().getName() + "\t" + "---come in ");
                try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); }
                return 20;
            });
            // future1和future2那个快用那个
            CompletableFuture thenCombineResult = completableFuture1.applyToEither(completableFuture2,f -> {
                System.out.println(Thread.currentThread().getName() + "\t" + "---come in ");
                return f + 1;
            });
    ​
            System.out.println(Thread.currentThread().getName() + "\t" + thenCombineResult.get());
        }
    
  5. 对计算结果进行合并

    // 两个CompletionStage任务都完成后,最终能把两个任务的结果一起交给thenCombine 来处理 
    // 先完成的先等待
    public static void test5() throws ExecutionException, InterruptedException {
            CompletableFuture<Integer> completableFuture1 = CompletableFuture.supplyAsync(() -> {
                System.out.println(Thread.currentThread().getName() + "\t" + "---come in ");
                //暂停几秒钟线程
                try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); }
                return 10;
            });
    ​
            CompletableFuture<Integer> completableFuture2 = CompletableFuture.supplyAsync(() -> {
                System.out.println(Thread.currentThread().getName() + "\t" + "---come in ");
                try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); }
                return 20;
            });
            // 任务1,任务2一起完成
            System.out.println(completableFuture1.thenCombine(completableFuture2, (f1, f2) -> {
                return f1 + f2;
            }).get());
    }