有了Future为什么还要CompletableFuture?

文章目录

  • Future 接口理论知识复习
    • Future 接口概述
    • 场景描述
    • 小结
  • Future 接口常用实现类 FutureTask 异步任务
    • Future 的作用
    • Futrue 编码测试
    • 优缺点分析
      • 优点
      • 缺点
      • 小结
    • 面对一些复杂的任务
      • 对于简单的业务场景使用 Future 接口完全 OK
      • 回调通知
      • 创建异步任务
      • 多个任务前后依赖可以组合
      • 对计算速度选最快
      • CompletableFuture 应运而生
  • CompletableFuture 对 Future 的改进
    • CompletableFuture 为什么会出现?
    • CompletableFuture 与 CompletionStage 源码
      • 类继承架构
      • 接口 CompletionStage
      • 类 CompletionFuture
    • 核心的四个静态方法,创建一个异步任务
      • runAsync 方法---无返回值
      • supplyAsync 方法---有返回值
      • 关于参数 Executor 说明
      • 减少阻塞和轮询
      • CompletableFuture 的优点
  • 电商网站比价需求案例
    • 函数式编程已经主流
    • Lambda 表达式+Stream 流式应用+Chain 链式调用+Java8 函数式编程
      • Runnable
      • Function
      • Consumer
      • Supplier
      • Summer
    • 先说说 join 和 get 的对比
    • 大厂业务需求说明
    • Java8 函数式编程在 Case 中的应用
      • 方案一,step by step
      • 方案二,asyncExecutor
      • 效果比较
  • CompletableFuture 的常用方法
    • 获得结果和触发计算
      • get()
      • get(long time,TimeUnit unit)
      • join()
      • getNow(String valueIfAbsent)
      • complete(T value)
    • 对计算结果进行处理
      • thenApply
      • handle
      • Summary
    • 对计算结果进行消费
      • demo
      • 补充
      • CompletableFuture 和线程池说明
    • 对计算速度进行选用
    • 对计算结果进行合并

Future 接口理论知识复习

Future 接口概述

  • Future 接口(FutureTask 实现类)定义了异步任务执行的一些方法
    • 获取异步任务执行的结果
    • 取消异步任务执行
    • 判断任务是否被取消,
    • 判断任务执行是否完毕等

场景描述

  • 主线程让一个子线程去执行任务,子线程可能比较耗时,如果没有实现异步任务执行,主线程只能一直等待
  • Future 接口支持了异步任务执行之后,子线程开始执行任务的同时,主线程继续执行自身任务,等到主线程或者子线程任务完成之后,主线程才会获取子线程任务执行结果
  • 上课买水案例…

小结

Future 接口可为主线程开启一个分支任务,专门为主线程处理耗时和废力的复杂业务

Future 接口常用实现类 FutureTask 异步任务

  • Future 接口是 Java5 新增的一个接口,提供了一种异步并行计算的功能
    • 若主线程需要执行一些很耗时的计算任务,可以通过 future 把该任务放到异步线程中去执行
    • 主线程继续处理其他任务或者先行结束,再通过 Future 获取计算结果

Future 的作用

  • 异步多线程任务执行且返回有结果,三个特点
    • 多线程
    • 有返回
    • 异步任务
  • 为什么是 Future?


Futrue 编码测试

public class CompletableFutureDemo {public static void main(String[] args) throws ExecutionException, InterruptedException {FutureTask<String> futureTask = new FutureTask<>(new MyThread());Thread t1 = new Thread(futureTask, "t1");t1.start();System.out.println(futureTask.get());}
}class MyThread implements Callable<String> {@Overridepublic String call() throws Exception {System.out.println("-----come in call() ");return "hello Callable";}
}

image.png

优缺点分析

优点

  • futrue+线程池异步多线程任务配合,能显著提高代码的执行效率
  • 串型执行
 //3个任务,目前只有一个线程main来处理,请问耗时多少?long startTime = System.currentTimeMillis();//暂停毫秒try {TimeUnit.MILLISECONDS.sleep(500);} catch (InterruptedException e) {e.printStackTrace();}try {TimeUnit.MILLISECONDS.sleep(300);} catch (InterruptedException e) {e.printStackTrace();}try {TimeUnit.MILLISECONDS.sleep(300);} catch (InterruptedException e) {e.printStackTrace();}long endTime = System.currentTimeMillis();System.out.println("----costTime: " + (endTime - startTime) + " 毫秒");System.out.println(Thread.currentThread().getName() + "\t -----end");

image.png

  • 使用 futureTask+线程池异步多线程任务
     ExecutorService threadPool = Executors.newFixedThreadPool(3);long startTime = System.currentTimeMillis();FutureTask<String> futureTask1 = new FutureTask<String>(() -> {try {TimeUnit.MILLISECONDS.sleep(500);} catch (InterruptedException e) {e.printStackTrace();}return "task1 over";});threadPool.submit(futureTask1);FutureTask<String> futureTask2 = new FutureTask<String>(() -> {try {TimeUnit.MILLISECONDS.sleep(300);} catch (InterruptedException e) {e.printStackTrace();}return "task2 over";});threadPool.submit(futureTask2);System.out.println(futureTask1.get());System.out.println(futureTask2.get());try {TimeUnit.MILLISECONDS.sleep(300);} catch (InterruptedException e) {e.printStackTrace();}long endTime = System.currentTimeMillis();System.out.println("----costTime: " + (endTime - startTime) + " 毫秒");System.out.println(Thread.currentThread().getName() + "\t -----end");threadPool.shutdown();

image.png

缺点

  • get 的获取容易阻塞
         FutureTask<String> futureTask = new FutureTask<String>(() -> {System.out.println(Thread.currentThread().getName() + "\t -----come in");try {TimeUnit.SECONDS.sleep(5);} catch (InterruptedException e) {e.printStackTrace();}return "task over";});Thread t1 = new Thread(futureTask, "t1");t1.start();System.out.println(Thread.currentThread().getName() + "\t ----忙其它任务了");System.out.println(futureTask.get());

image.png

  • get容易导致阻塞,一般建议放在程序后面,一旦调用不见不散,非要等到结果才会离开,不管你是否计算完成,容易程序堵塞。
        System.out.println(futureTask.get());System.out.println(Thread.currentThread().getName() + "\t ----忙其它任务了");

image.png

  • 假如我不愿意等待很长时间,我希望过时不候,可以自动离开.
        System.out.println(Thread.currentThread().getName() + "\t ----忙其它任务了");System.out.println(futureTask.get(3,TimeUnit.SECONDS));
  • 超过 3 秒结束线程抛出 TimeOutException

image.png

  • 轮询耗费 CPU
        while (true) {if (futureTask.isDone()) {//futureTask执行完成System.out.println(futureTask.get());break;} else {//暂停毫秒,未完成,持续等待try {TimeUnit.MILLISECONDS.sleep(500);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("正在处理中,不要再催了,越催越慢 ,再催熄火");}}

image.png

小结

  • get(),一旦调用 get 方法求结果,如果计算没有完成,容易导致线程阻塞
  • isDone()轮询
    • 轮询的方式会消耗无谓的 CPU 资源,而且也不见得能及时得到计算结果
    • 如果想要异步获取,通常都会以轮询的方式去获取结果,尽量不使用阻塞
  • Future 对于结果的获取不是很友好,只能通过阻塞或轮询的方式得到结果

面对一些复杂的任务

对于简单的业务场景使用 Future 接口完全 OK

回调通知

  • 应对 Future 的完成时间,完成之后发起回调通知
  • 通过轮询方式去判断任务是否完成,非常不优雅,也占用 CPU

创建异步任务

  • Future+线程池配合

多个任务前后依赖可以组合

  • 若想将多个异步任务的计算结果组合起来,则后一个异步任务的计算结果,需要前一个异步任务的值
  • 将两个或多个异步计算合成一个异步计算,这几个异步计算,互相独立,同时后面这个又依赖于前一个处理的结果

对计算速度选最快

  • 当 Future 集合中某个任务最快结束时,返回结果,返回第一名处理结果

CompletableFuture 应运而生

  • 使用 Future 接口提供的 API,处理不够优雅

image.png

  • CompletableFuture 以声明式方式优雅的处理这些需求同时规避 Future 自身获取计算结果的弊端

CompletableFuture 对 Future 的改进

CompletableFuture 为什么会出现?

  • get()方法在 Future 计算完成之前会一直处于阻塞状态下
  • isDone()方法容易耗费 CPU 资源
  • 对于真正在异步处理中我们希望可以通过传入回调函数,在 Future 结束时自动回调该函数,这样就不需要等待结果
  • 阻塞的方式和异步编程的设计理念相违背,而轮询的方式会耗费无谓的 CPU 资源
  • 因此,JDK8 设计出 CompletableFuture
    • CompletableFuture 提供了一种与观察者模式类似的机制,可以让任务执行完成后通知监听的一方

CompletableFuture 与 CompletionStage 源码

类继承架构

public class CompletableFuture<T> implements Future<T>, CompletionStage<T>{}

image.png

接口 CompletionStage

  • CompletionStage 代表异步计算过程中的某个阶段,一个阶段完成以后会触发另一个阶段(类似于 Linux 管道分隔符传参数)
  • 一个阶段的计算执行可以是一个 Function,Consumer 或者 Runnable
//示例如下
stage
.thenApply(x->square(x))
.thenAccept(x->System.out.print(x))
.thenRun(()->System.out.println())
  • 一个阶段的执行可能被单个阶段的完成触发,也可能有多个阶段一起触发

类 CompletionFuture

  • Java8 中,CompletableFuture 提供了非常强大的 Future 的扩展功能,简化异步编程的复杂性,并且提供了函数式编程的能力,可以通过回调的方式处理计算结果,提供了转化和组合 CompletionFuture 的方法
  • 它可能代表了一个明确完成 Future,也可能代表一个完成阶段 CompletionStage,它支持在计算完成之后触发一些函数或执行某些动作
  • 实现了 Future 和 CompletionStage 接口

核心的四个静态方法,创建一个异步任务

  • 为什么要不用 new CompletionFuture()方式创建异步任务

image.png

  • API 中说明通过 new CompletionFuture()方式会创建一个不完备的 CompletionFuture,官方也不推荐使用该方式

runAsync 方法—无返回值

  • public static CompletableFuture<Void> runAsync(Runnable runnable)
    • 使用默认的 ForkJoinPool 线程池
        CompletableFuture<Void> completableFuture = CompletableFuture.runAsync(() -> {System.out.println(Thread.currentThread().getName());//暂停几秒钟线程try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}});System.out.println(completableFuture.get());

image.png

  • public static CompletableFuture<Void> runAsync(Runnable runnable,Executor executor)
    • 使用定义的线程池对象
        ExecutorService threadPool = Executors.newFixedThreadPool(3);CompletableFuture<Void> completableFuture = CompletableFuture.runAsync(() -> {System.out.println(Thread.currentThread().getName());//暂停几秒钟线程try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}},threadPool);System.out.println(completableFuture.get());threadPool.shutdown();

image.png

supplyAsync 方法—有返回值

  • public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier)
        CompletableFuture<String> completableFuture = CompletableFuture.supplyAsync(() -> {System.out.println(Thread.currentThread().getName());//暂停几秒钟线程try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}return "hello supplyAsync";});System.out.println(completableFuture.get());

image.png

  • public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier,Executor executor)
        ExecutorService threadPool = Executors.newFixedThreadPool(3);CompletableFuture<String> completableFuture = CompletableFuture.supplyAsync(() -> {System.out.println(Thread.currentThread().getName());//暂停几秒钟线程try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}return "hello supplyAsync";},threadPool);System.out.println(completableFuture.get());threadPool.shutdown();

image.png

关于参数 Executor 说明

  • 没有指定 Executor 的方法,直接默认使用 ForkJoinPool.commonPool()作为线程池,作为它的线程池执行异步代码
  • 若指定线程池,则使用自定义或者特别定义的线程池执行异步代码

减少阻塞和轮询

  • 从 Java8 开始引入了 CompletableFuture,它是 Future 的功能增强版,减少阻塞和轮询,
  • 可以传入回调对象,当异步任务完成或者发生异常时,自动回调对象的回调方法
  • 使用 CompletableFuture 实现 Future 的功能
CompletableFuture<Integer> completableFuture = CompletableFuture.supplyAsync(() -> {System.out.println(Thread.currentThread().getName() + "----come in");int result = ThreadLocalRandom.current().nextInt(10);try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("-----1秒钟后出结果:" + result);return result;});System.out.println(Thread.currentThread().getName() + "线程先去忙其它任务");System.out.println(completableFuture.get());

image.png

  • CompletableFuture.supplyAsync()完成异步编程返回结果
try {CompletableFuture.supplyAsync(() -> {System.out.println(Thread.currentThread().getName() + "----come in");int result = ThreadLocalRandom.current().nextInt(10);try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("-----1秒钟后出结果:" + result);return result;}).whenComplete((v, e) -> {//v为上述计算完成的result,e为异常if (e == null) { //没有异常System.out.println("-----计算完成,更新系统UpdateValue:" + v);}}).exceptionally(e -> {e.printStackTrace();System.out.println("异常情况:" + e.getCause() + "\t" + e.getMessage());return null;});System.out.println(Thread.currentThread().getName() + "线程先去忙其它任务");} catch (Exception e) {e.printStackTrace();} finally {threadPool.shutdown();}

image.png

  • 解释下为什么默认线程池关闭,自定义线程池记得关闭?
  • 用户线程中,程序执行完成需要 1 秒钟,main 线程执行太快,在 ForkJoinPool 线程池中若发现 main 线程执行完成则会关闭线程池
  • 解决方法
    • 将 main 线程延迟 1 秒,在用户线程的 try/catch/finally 代码之后添加睡眠代码
        //主线程不要立刻结束,否则CompletableFuture默认使用的线程池会立刻关闭:暂停3秒钟线程try {TimeUnit.SECONDS.sleep(3);} catch (InterruptedException e) {e.printStackTrace();}

image.png

  • 或是使用定义的线程对象,或是自定义线程对象
        ExecutorService threadPool = Executors.newFixedThreadPool(3);try {CompletableFuture.supplyAsync(() -> {System.out.println(Thread.currentThread().getName() + "----come in");int result = ThreadLocalRandom.current().nextInt(10);try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("-----1秒钟后出结果:" + result);return result;},threadPool).whenComplete((v, e) -> {if (e == null) {System.out.println("-----计算完成,更新系统UpdateValue:" + v);}}).exceptionally(e -> {e.printStackTrace();System.out.println("异常情况:" + e.getCause() + "\t" + e.getMessage());return null;});System.out.println(Thread.currentThread().getName() + "线程先去忙其它任务");} catch (Exception e) {e.printStackTrace();} finally {threadPool.shutdown();}

image.png

  • 编写异常代码测试

image.png
image.png

CompletableFuture 的优点

  • 异步任务结束时,会自动调用对象的方法
  • 主线程设置好回调之后,不在关系异步任务的执行,异步任务之间可以顺序进行
  • 异步任务出错时,会自动调用某个对象的方法
        try {//调用异步任务,传入线程池对象asyncTask(threadPool);System.out.println(Thread.currentThread().getName() + "线程先去忙其它任务");} catch (Exception e) {e.printStackTrace();} finally {threadPool.shutdown();}//...主线程void asyncTask(ExecutorService threadPool) {//...业务的逻辑return result;}, threadPool).whenComplete((v, e) -> {//回调接口callInterface(v, e);}).exceptionally(e -> {//异常接口e.printStackTrace();exceptionHandel(e);return null;});}

电商网站比价需求案例

函数式编程已经主流

  • 大厂面试题

image.png

Lambda 表达式+Stream 流式应用+Chain 链式调用+Java8 函数式编程

Runnable

  • 无参数,无返回值
package java.lang;@FunctionalInterface
public interface Runnable {public abstract void run();
}

Function

  • Function<T,R>接受一个参数,并且有返回值
@FunctionalInterface
public interface Function<T, R> {R apply(T t);
}

Consumer

  • Consumer 接受一个参数,没有返回值
@FunctionalInterface
public interface Consumer<T> {void accept(T t);
}
  • BiConsumer<T,U>接受两个参数,没有返回值
@FunctionalInterface
public interface BiConsumer<T, U> {void accept(T t, U u);
}
  • 在回调 CompletableFuture.whenComplete 方法中进行调用

image.png

Supplier

  • 供给型函数式接口,没有参数,有一个返回值
@FunctionalInterface
public interface Supplier<T> {/*** Gets a result.** @return a result*/T get();
}

Summer

image.png

先说说 join 和 get 的对比

  • get 在程序编译时会检查异常,join 在程序编译时不会检查异常,此外于 get 基本等价
        CompletableFuture<String> supplyAsync = CompletableFuture.supplyAsync(() -> {return "join and get";});System.out.println(supplyAsync.join());

image.png

  • 说说你过去工作中的项目亮点!!!(面试必备)

大厂业务需求说明

  • 切记,先完成功能再到性能的逐步迭代
  • 电商网站比价需求分析
案例说明:电商比价需求,模拟如下情况:1. 需求:1.1 同一款产品,同时搜索出同款产品在各大电商平台的售价;1.2 同一款产品,同时搜索出本产品在同一个电商平台下,各个入驻卖家售价是多少2. 输出:出来结果希望是同款产品的在不同地方的价格清单列表,返回一个List<String>《mysql》 in jd price is 88.05《mysql》 in dangdang price is 86.11《mysql》 in taobao price is 90.433. 解决方案,对别同一个商品在各个平台上的价格,要求获得一个清单列表3.1. step by step,按部就班查完jd,查taobao,查完taobao查天猫.....3.2. all in ,使用多线程,异步任务执行同时查询多个平台4. 技术要求3.1 函数式编程3.2 链式编程3.3 Stream流式计算

Java8 函数式编程在 Case 中的应用

  • 创建资源
    • 电商网站类
//电商网站类
class NetMall {/*** 电商网站名 jd,pdd taobao...*/@Getterprivate String netMallName;/*** 构造方法* @param netMallName*/public NetMall(String netMallName) {this.netMallName = netMallName;}/*** 售价* @param productName* @return*/public double calcPrice(String productName) {try {//查询需要1秒钟TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}//模拟价格return ThreadLocalRandom.current().nextDouble() * 2 + productName.charAt(0);}
}
  • 查询电商网站
    static List<NetMall> list = Arrays.asList(new NetMall("jd"),new NetMall("dangdang"),new NetMall("taobao"),new NetMall("pdd"),new NetMall("tmall"));

方案一,step by step

  • 使用流式计算,查询返回结果
 /*** step by step 一家家搜查* List<NetMall> ----->map------> List<String>** @param list* @param productName* @return*/public static List<String> getPrice(List<NetMall> list, String productName) {//《mysql》 in taobao price is 90.43return list.stream()//流式计算.map(netMall -> //映射为map集合//字符串格式化String.format(productName + " in %s price is %.2f",netMall.getNetMallName(),netMall.calcPrice(productName))).collect(Collectors.toList());}
  • 测试计算结果
 public static void main(String[] args) {long startTime = System.currentTimeMillis();List<String> list1 = getPrice(list, "mysql");for (String element : list1) {System.out.println(element);}long endTime = System.currentTimeMillis();System.out.println("----costTime: " + (endTime - startTime) + " 毫秒");}

image.png

方案二,asyncExecutor

  • 基于CompletableFuture.supplyAsync
/*** List<NetMall> ----->List<CompletableFuture<String>>------> List<String>** @param list* @param productName* @return*/public static List<String> getPriceByCompletableFuture(List<NetMall> list, String productName) {return list.stream().map(netMall ->CompletableFuture.supplyAsync(() -> String.format(productName + " in %s price is %.2f",netMall.getNetMallName(),netMall.calcPrice(productName)))).collect(Collectors.toList()).stream().map(s -> s.join()).collect(Collectors.toList());}
  • 两次流式映射

image.png

public static void main(String[] args) {long startTime2 = System.currentTimeMillis();List<String> list2 = getPriceByCompletableFuture(list, "mysql");for (String element : list2) {System.out.println(element);}long endTime2 = System.currentTimeMillis();System.out.println("----costTime: " + (endTime2 - startTime2) + " 毫秒");
}

效果比较

image.png

CompletableFuture 的常用方法

获得结果和触发计算


get()

 /*** 获得结果和触发计算** @throws InterruptedException* @throws ExecutionException*/private static void group1() throws InterruptedException, ExecutionException {CompletableFuture<String> completableFuture = CompletableFuture.supplyAsync(() -> {//暂停几秒钟线程try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}return "abc";});System.out.println(completableFuture.get());}

image.png

get(long time,TimeUnit unit)

System.out.println(completableFuture.get(2L,TimeUnit.SECONDS));

image.png

join()

System.out.println(completableFuture.join());

image.png

getNow(String valueIfAbsent)

System.out.println(completableFuture.getNow("xxx"));

image.png

  • 源码解读
    • 当调用 getNow 时,计算完成,获取计算结果
    • 当调用 getNow 时,计算未完成,返回备选值(valueIfabsent)

image.png

  • 异步任务执行 1s,主线程 2s,在 ,异步任务在 2s 内执行完成,返回结果给 getNow
        CompletableFuture<String> completableFuture = CompletableFuture.supplyAsync(() -> {//暂停几秒钟线程try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}return "abc";});//暂停几秒钟线程try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); }System.out.println(completableFuture.getNow("xxx"));

image.png

complete(T value)

        CompletableFuture<String> completableFuture = CompletableFuture.supplyAsync(() -> {//暂停几秒钟线程try {//执行2sTimeUnit.SECONDS.sleep(2);} catch (InterruptedException e) {e.printStackTrace();}return "abc";});//执行1stry { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); }System.out.println(completableFuture.complete("completeValue") + "\t" + completableFuture.join());

image.png

  • 源码说明

image.png

  • 主线程调用异步任务时
    • 计算未完成,返回 true,同时将 value 作为 result 给到主线程
    • 计算完成,返回 false,同时将异步任务的计算结果给到主线程
  • 将异步任务与主线程的睡眠时间互换,得到以下结果

image.png

对计算结果进行处理

  • 此处 supplyAsync 若不使用指定线程池,主线程执行完会直接结束 jvm

thenApply

  • 计算结果存在依赖关系,这两个线程串行化
  • demo
private static void thenApply1(ExecutorService threadPool) {CompletableFuture.supplyAsync(() -> {//暂停几秒钟线程try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("111");return 1;}, threadPool).thenApply(f -> {System.out.println("222");return f + 2;}).thenApply(f -> {System.out.println("333");return f;}).whenComplete((v, e) -> {if (e == null) {System.out.println("----计算结果: " + v);}}).exceptionally(e -> {e.printStackTrace();System.out.println(e.getMessage());return null;});System.out.println(Thread.currentThread().getName() + "----主线程先去忙其它任务");threadPool.shutdown();}

image.png

  • f=1+2=3
  • 异常处理
    • 计算过程中出现异常,thenApply(),会直接终止计算

image.png
image.png

handle

  • 计算结果存在依赖关系,这两个线程串行化
  • demo
private static void handle1(ExecutorService threadPool) {CompletableFuture.supplyAsync(() -> {//暂停几秒钟线程try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("111");return 1;}, threadPool).handle((f, e) -> {System.out.println("222");return f + 2;}).handle((f, e) -> {System.out.println("333");return f + 3;}).whenComplete((v, e) -> {if (e == null) {System.out.println("----计算结果: " + v);}}).exceptionally(e -> {e.printStackTrace();System.out.println(e.getMessage());return null;});System.out.println(Thread.currentThread().getName() + "----主线程先去忙其它任务");threadPool.shutdown();}

image.png

  • 异常处理
    • 有异常时,跳过异常代码,带着异常参数继续执行后续代码

image.png
image.png

Summary

image.png

对计算结果进行消费

  • 接受任务的处理结果,并消费处理,无返回结果

demo

  • 源码解读

image.png

  • 调用了 Consumer 接口,传入参数无返回值
    public static void main(String[] args) {CompletableFuture.supplyAsync(() -> {return 1;}).thenApply(f ->{return f + 2;}).thenApply(f ->{return f + 3;}).thenAccept(System.out::println);}

image.png

补充

  • thenRun 不调用前置计算的结果

image.png

  • thenAccpet 获取前置计算结果,最终不返回记过,consumer 直接消费

image.png

  • thenApply,获取前置计算结果,最终返回所有计算结果

image.png

CompletableFuture 和线程池说明

  • 以 thenRun 和 thenRunAsync 为例
    public static void main(String[] args) {ExecutorService threadPool = Executors.newFixedThreadPool(5);try {CompletableFuture<Void> completableFuture = CompletableFuture.supplyAsync(() -> {try {TimeUnit.MILLISECONDS.sleep(20);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("1号任务" + "\t" + Thread.currentThread().getName());return "abcd";}, threadPool).thenRunAsync(() -> {try {TimeUnit.MILLISECONDS.sleep(20);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("2号任务" + "\t" + Thread.currentThread().getName());}).thenRun(() -> {try {TimeUnit.MILLISECONDS.sleep(10);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("3号任务" + "\t" + Thread.currentThread().getName());}).thenRun(() -> {try {TimeUnit.MILLISECONDS.sleep(10);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("4号任务" + "\t" + Thread.currentThread().getName());});System.out.println(completableFuture.get(2L, TimeUnit.SECONDS));} catch (Exception e) {e.printStackTrace();} finally {threadPool.shutdown();}}

image.png

  • 将所有的方法都统一为 thenRun

image.png

  • 将入口睡眠代码注释

image.png

  • 结论
    • 没有传入自定义线程池,都默认使用 ForkJoinPool
    • 传入一个指定线程池之后
      • 执行第一个任务时,传入指定线程池
        • 调用 thenRun 方法执行第二个任务时,则第一个任务和第二个任务共用同一个线程池
        • 调用 thenRunAsync 执行第二个任务时,则第一个任务用指定线程池,第二个任务用 ForkJoinPool
    • 有可能处理太快,系统优化切换原则直接使用 main 线程处理
    • 其它 thenAccept 与 thenAccpetAsync,thenApply 和 thenApplyAsync 等,之间的区别亦是同理
  • 源码解读

image.png image.png

  • 调用 thenXxxxAsync 方法默认都会调用一个 ForkJoinPool.commonPool()

对计算速度进行选用

  • 谁快用谁
  • applyToEither
    public static void main(String[] args) {//开启两个异步任务CompletableFuture<String> playA = CompletableFuture.supplyAsync(() -> {System.out.println("A come in");try {TimeUnit.SECONDS.sleep(3);} catch (InterruptedException e) {e.printStackTrace();}return "playA";});CompletableFuture<String> playB = CompletableFuture.supplyAsync(() -> {System.out.println("B come in");try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}return "playB";});//比较两个异步任务,返回率先完成计算的异步任务的结果CompletableFuture<String> result = playA.applyToEither(playB, f -> {return f + " is winer";});System.out.println(Thread.currentThread().getName() + "\t" + "-----: " + result.join());}

image.png
1707042003737.png

对计算结果进行合并

  • 两个 CompletionStage 任务都完成后,最终能把两个任务的结果一起交给 thenCombine 进行处理
  • 先完成的先等待,所有分支完成后执行 thenCombine
  • 拆分方式
  public static void main(String[] args) {CompletableFuture<Integer> completableFuture1 = CompletableFuture.supplyAsync(() -> {System.out.println(Thread.currentThread().getName() + "\t ---启动");//暂停几秒钟线程try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}return 10;});CompletableFuture<Integer> completableFuture2 = CompletableFuture.supplyAsync(() -> {System.out.println(Thread.currentThread().getName() + "\t ---启动");//暂停几秒钟线程try {TimeUnit.SECONDS.sleep(2);} catch (InterruptedException e) {e.printStackTrace();}return 20;});CompletableFuture<Integer> result = completableFuture1.thenCombine(completableFuture2, (x, y) -> {System.out.println("-----开始两个结果合并");return x + y;});System.out.println(result.join());}

image.png

  • 函数式接口方式
private static void interfaceChain() {ExecutorService threadPool = Executors.newFixedThreadPool(3);CompletableFuture<Integer> thenCombineResult = CompletableFuture.supplyAsync(() -> {System.out.println(Thread.currentThread().getName() + "\t"+"come in 1");return 10;}).thenCombine(CompletableFuture.supplyAsync(()->{System.out.println(Thread.currentThread().getName() + "\t"+"come in 2");return 20;}),(x,y)->{System.out.println(Thread.currentThread().getName() + "\t"+"x + y = a =" +(x+y));return x + y ;}).thenCombine(CompletableFuture.supplyAsync(()->{System.out.println(Thread.currentThread().getName() + "\t"+"come in 3");return 30;}),(a,b)->{System.out.println(Thread.currentThread().getName() + "\t"+"a + b = " +(a+b));return a+b;});System.out.println("---主线程结束,END");System.out.println(thenCombineResult.join());}

image.png


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/667358.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

DFS——连通性和搜索顺序

dfs的搜索是基于栈&#xff0c;但一般可以用用递归实现&#xff0c;实际上用的是系统栈。有内部搜索和外部搜索两种&#xff0c;内部搜索是在图的内部&#xff0c;内部搜索一般基于连通性&#xff0c;从一个点转移到另一个点&#xff0c;或者判断是否连通之类的问题&#xff0c…

[Python] opencv - 什么是直方图?如何绘制图像的直方图?

什么是直方图&#xff1f; 直方图是一种统计图&#xff0c;用于展示数据的分布情况。它将数据按照一定的区间或者组进行划分&#xff0c;然后计算在每个区间或组内的数据频数或频率&#xff08;即数据出现的次数或占比&#xff09;&#xff0c;然后用矩形或者柱形图的形式将这…

C++学习Day03之构造函数和析构函数

目录 一、程序及输出1.1 构造函数1.2 析构函数1.3 构造和析构必须要声明在全局作用域 二、分析与总结 一、程序及输出 1.1 构造函数 构造函数 没有返回值 不用写void 函数名 与 类名相同 可以有参数 &#xff0c;可以发生重载 构造函数 由编译器自动调用一次 无须手动调用 创建…

C语言——Q/编译和链接

目录 一、翻译环境和运⾏环境 二、翻译环境 1、预处理&#xff08;预编译&#xff09; 2、编译 2.2.1 词法分析&#xff1a; 2.2.2 语法分析 2.2.3 语义分析 3、汇编 4、链接 三、运行环境 一、翻译环境和运行环境 在ANSI C 的任何⼀种实现中&#xff0c;存在两个不…

CentOS7局域网内搭建本地yum源

CentOS7.6 局域网内搭建本地yum源 一、背景 客户机房服务器无法直连公网&#xff0c;远程通过堡垒机部署环境&#xff0c;因为机器比较多&#xff0c;最终选择通过安装自定义yum源进行部署。以下为自己部署yum源过程&#xff0c;以备后续使用。 二、准备yum源Packages 网上…

【刷题题解】最长回文子序列

给你一个字符串 s &#xff0c;找出其中最长的回文子序列&#xff0c;并返回该序列的长度。 子序列定义为&#xff1a;不改变剩余字符顺序的情况下&#xff0c;删除某些字符或者不删除任何字符形成的一个序列 这道题&#xff0c;一眼动态规划&#xff0c;但是即使动起来也规划…

python_蓝桥杯刷题记录_笔记_全AC代码_入门3

前言 记录我的解法以及笔记思路&#xff0c;谢谢观看。 题单目录 1.P2141 [NOIP2014 普及组] 珠心算测验 2.P1567 统计天数 3.P1055 [NOIP2008 普及组] ISBN 号码 4.P1200 [USACO1.1] 你的飞碟在这儿 Your Ride Is Here 5.P1308 [NOIP2011 普及组] 统计单词数 6.P1047 […

深度学习之循环神经网络进阶

这一讲我们学习如何实现一个循环神经网络的分类器&#xff1a; 我们要解决的问题是名字分类&#xff0c;我们根据名字找到其对应的国家。 上一讲我们介绍了循环神经网络。 我们在处理自然语言的时候我们通常是以上这种方式&#xff0c;我们在处理单词的时候&#xff0c;通常…

一文彻底搞懂MySQL基础:B树和B+树的区别(简洁版)

文章目录 1. 节点结构2. 插入和删除3. 查询4. 性能5. 适用场景6.关于 B树和 B树的常见问题6.1. B树和B树的区别是什么&#xff1f;6.2. 什么情况下应该使用 B树&#xff1f;6.3. 什么情况下应该使用 B树&#xff1f; B树和B树都是多路搜索树&#xff0c;它们都用于数据库索引中…

命令注入漏洞原理以及修复方法

漏洞名称 &#xff1a;命令注入 漏洞描述&#xff1a;Command Injection&#xff0c;即命令注入攻击&#xff0c;是指由于Web应用程序对用户提交的数据过滤 不严格&#xff0c;导致黑客可以通过构造特殊命令字符串的方式&#xff0c;将数据提交至Web应用程序中&#xff0c;并利…

一文讲明Jetpack中的图片组件

Jetpack Compose系列(5) - 图片组件 Jetpack Compose中的常用图片组件有两个&#xff1a;Icon和Image。从命名上就不难看出这两个组件在内容呈现上就是负责图形和图片相关。 需要说明的是&#xff0c;Compose获取资源方式有四种&#xff1a; 文本 -> stringResource(R.s…

比特币ETF广告战大爆发!

作者&#xff1a;秦晋 贝莱德主动发起广告攻势。 2月1日&#xff0c;据外媒Cryptoslate报道&#xff0c;贝莱德在提交给美国SEC的一份文件中显示&#xff0c;其提出一项在建筑物侧面投影比特币ETF广告计划。 据介绍&#xff0c;广告内容为&#xff1a;「IBIT」信号是一个以迈阿…

【Qt+MSVC2017_64bit +Cmake新建项目编译出错】

项目场景&#xff1a; 提示&#xff1a;这里简述项目相关背景&#xff1a; 项目新电脑环境配置 QtMSVC2017_64bit Cmake新建项目编译出错 问题描述 提示&#xff1a;这里描述项目中遇到的问题&#xff1a; QtMSVC2017_64bit Cmake新建项目编译出错 Running C:\Program Fil…

【数据分享】1米分辨率土地覆盖数据集SinoLC-1

数据链接 SinoLC-1: the first 1-meter resolution national-scale land-cover map of China created with the deep learning framework and open-access data (Update data: August, 2023) (zenodo.org)https://zenodo.org/records/8214467 数据分享 数据分享到了公众号&…

Android studio改代码运行不生效

Android studio改代码后运行不生效&#xff0c;尝试卸载apk后&#xff0c;运行能生效&#xff0c;后面尝试手动通过adb命令安装生成的apk能生效。 studio 版本 解决方案&#xff1a; 在File->Settings->Build, Execution, Deployment&#xff0c;找到Android Configura…

BFS——双向广搜+A—star

有时候从一个点能扩展出来的情况很多&#xff0c;这样几层之后搜索空间就很大了&#xff0c;我们采用从两端同时进行搜索的策略&#xff0c;压缩搜索空间。 190. 字串变换(190. 字串变换 - AcWing题库) 思路&#xff1a;这题因为变化规则很多&#xff0c;所以我们一层一层往外…

golang开源的可嵌入应用程序高性能的MQTT服务

golang开源的可嵌入应用程序高性能的MQTT服务 什么是MQTT&#xff1f; MQTT&#xff08;Message Queuing Telemetry Transport&#xff09;是一种轻量级的、开放的消息传输协议&#xff0c;设计用于在低带宽、高延迟或不可靠的网络环境中进行通信。MQTT最初由IBM开发&#xf…

canvas设置图形各种混合模式,类似photoshop效果

查看专栏目录 canvas实例应用100专栏&#xff0c;提供canvas的基础知识&#xff0c;高级动画&#xff0c;相关应用扩展等信息。canvas作为html的一部分&#xff0c;是图像图标地图可视化的一个重要的基础&#xff0c;学好了canvas&#xff0c;在其他的一些应用上将会起到非常重…

一看就会《幻兽帕鲁》服务器自建指南

玩转幻兽帕鲁服务器&#xff0c;阿里云推出新手0基础一键部署幻兽帕鲁服务器教程&#xff0c;傻瓜式一键部署&#xff0c;3分钟即可成功创建一台Palworld专属服务器&#xff0c;成本仅需26元&#xff0c;阿里云服务器网aliyunfuwuqi.com分享2024年新版基于阿里云搭建幻兽帕鲁服…