【JUC】使用CompletableFuture执行异步任务

文章目录

  • Future接口介绍
  • Future接口常用实现类FutureTask
    • Future接口能干什么
    • Future接口相关架构
    • FutureTask初步使用
    • Future编码实战和优缺点分析
      • 优点
      • 缺点
        • 获取结果的方式不优雅
        • 结论
      • 完成一些复杂的任务
  • CompletableFuture对Future的改进
    • CompletableFuture为什么会出现
    • CompletableFuture和CompletionStage介绍
    • 创建异步任务的四个静态方法
      • runAsync
      • supplyAsync
      • 实例
    • 任务完成回调+任务异常
    • CompletableFuture优点
  • 案例精讲:从电商网站的比价需求展开
    • 函数式编程已成为主流
      • **函数式接口**
      • **chain链式调用**
    • 大厂业务需求说明
    • 一波流Java8函数式编程带走-比价案例实战Case
  • CompletableFuture常用方法
    • **获得结果和触发计算**
      • 获取结果`get`、`join`、`getNow`
        • join()和get()的区别
      • 主动触发计算`complete`
    • **对计算结果进行处理(串行化)**
      • `thenApply`
      • `handle`
    • **对计算结果进行消费**
      • thenAccept
    • thenRun、thenAccept、thenApply对比
    • 有无Async对比
      • 源码分析
    • **对计算速度进行选用applyToEither**
    • 两任务组合
      • 两个任务完成才做下一个
        • **对计算结果进行合并thenCombine**
      • A、B中一个完成就做下一个
    • 多任务组合
      • allOf:等待所有任务完成
      • anyOf:只要有一个任务完成

Future接口介绍

在这里插入图片描述

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

在这里插入图片描述

  • Future接口可以为主线程开一个分支任务,专门为主线程处理耗时和费力的复杂业条。比如主线程让一个子线程去执行任务,子线程可能比较耗时,启动子线程开始执行任务后,主线程就去做其他事情了,忙完其他事情或者先执行完,过了一会再才去获取子任务的执行结果或变更的任务状态(如老师上课时间想喝水,他继续讲课不结束上课这个主线程,让学生去小卖部帮老师买水完成这个耗时和费力的任务)。

Future接口常用实现类FutureTask

Future接口能干什么

Future是Java5新加的一个接口,它提供一种异步并行计算的功能,如果主线程需要执行一个很耗时的计算任务,我们会就可以通过Future把这个任务放进异步线程中执行,主线程继续处理其他任务或者先行结束,再通过Future获取计算结果。

Future接口相关架构

在这里插入图片描述

  • 目的:异步多线程任务执行且返回有结果,三个特点:多线程、有返回、异步任务(班长为老师去买水作为新启动的异步多线程任务且买到水有结果返回)
  • 代码实现:Runnable接口+Callable接口+Future接口和FutureTask实现类。

Runnable接口和Callable接口的区别

在这里插入图片描述

Thread要创建一个线程,只能接受Runnable接口,不能满足有返回值、异形任务(需要满足能获取异步任务的执行结果、取消异步任务的执行、判断任务是否被取消、判断任务执行是否完毕等特点)的需求。

在这里插入图片描述

在这里插入图片描述

RunnableFuture同时有了Runnable和Future的功能,即能实现多线程、异步任务

在这里插入图片描述

RunnableFuture有一个实现类FutureTask,FutureTask可以通过构造方法传入Callable,所以可以同时实现多线程、有返回、异步任务

在这里插入图片描述

在这里插入图片描述

FutureTask初步使用

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.start();// 获取返回值 返回hello CallableSystem.out.println(futureTask.get()); }
}
class MyThread implements Callable<String> {@Overridepublic String call() throws Exception {System.out.println("--------come in call");return "hello Callable";}
}

在这里插入图片描述

Future编码实战和优缺点分析

优点

Future+线程池异步多线程任务配合,能显著提高程序的运行效率。

public class FutureThreadPoolDemo {/*** 3个任务,目前开启多个异步任务线程来处理,请问耗时多少?883毫秒* @param args* @throws ExecutionException* @throws InterruptedException*/public static void main(String[] args) throws ExecutionException, InterruptedException {// 线程池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();}/*** 3个任务,目前只有一个线程main来处理,请问耗时多少?1131毫秒*/private static void m1() {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");}
}

缺点

获取结果的方式不优雅
  • get()阻塞:一旦调用get()方法求结果,一旦调用就要等到结果才会离开,不管你是否计算完成,如果没有计算完成容易程序堵塞。
public class FutureApiDemo {public static void main(String[] args) throws ExecutionException, InterruptedException, TimeoutException {FutureTask<String> futureTask = new FutureTask<>(() -> {System.out.println(Thread.currentThread().getName() + "--------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(futureTask.get());System.out.println(Thread.currentThread().getName() + " ------忙其他任务");}
}

在这里插入图片描述

可以设置等待三秒,futureTask.get(3,TimeUnit.SECONDS),过时会抛出超时异常,其他代码捕获到这个异常可以继续执行,但是在实际场景中可能会留下大量的日志,不推荐使用

在这里插入图片描述

  • isDone()轮询:轮询的方式会耗费无谓的cpu资源,而且也不见得能及时得到计算结果,如果想要异步获取结果,通常会以轮询的方式去获取结果,尽量不要阻塞。
public class FutureApiDemo {public static void main(String[] args) throws ExecutionException, InterruptedException, TimeoutException {FutureTask<String> futureTask = new FutureTask<>(() -> {System.out.println(Thread.currentThread().getName() + "--------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(futureTask.get());//这样会有阻塞的可能,在程序没有计算完毕的情况下。System.out.println(Thread.currentThread().getName() + " ------忙其他任务");
//        System.out.println(futureTask.get(3,TimeUnit.SECONDS));//只愿意等待三秒,计算未完成直接抛出异常while (true) {//轮询if(futureTask.isDone()){System.out.println(futureTask.get());break;}else{// 500毫秒之后再看看有没有结果TimeUnit.MILLISECONDS.sleep(500);System.out.println("正在处理中,不要催了,越催越慢,再催熄火");}}/* 轮询结果* main ------忙其他任务t1--------come in正在处理中,不要催了,越催越慢正在处理中,不要催了,越催越慢正在处理中,不要催了,越催越慢正在处理中,不要催了,越催越慢正在处理中,不要催了,越催越慢正在处理中,不要催了,越催越慢正在处理中,不要催了,越催越慢正在处理中,不要催了,越催越慢正在处理中,不要催了,越催越慢正在处理中,不要催了,越催越慢task overProcess finished with exit code 0* */}
}
结论
  • Future对于结果的获取不是很友好,只能通过阻塞或轮询的方式得到任务的结果。(CompletableFuture诞生的原因)

完成一些复杂的任务

需求:

  • 对于简单的业务场景使用Future完全ok,高并发业务使用的话,阻塞和轮询都会影响性能
  • 通过轮询的方式去判断任务是否完成这样非常占cpu并且代码也不优雅。希望可以有回调通知,即应对Future的完成时间,完成了可以告诉我
  • 创建异步任务:Future+线程池组合
  • 多个任务前后依赖可以组合处理
    • 想将多个异步任务的结果组合起来,后一个异步任务的计算结果需要前一个异步任务的值
    • 想将两个或多个异步计算合并成为一个异步计算,这几个异步计算互相独立,同时后面这个又依赖前一个处理的结果(水煮鱼:买鱼—>调料—>下锅)
  • 对计算速度选最快的:当Future集合中某个任务最快结束时,返回该结果

结论

  • 想要实现上述需求,使用Future之前提供的那点API就囊中羞涩,处理起来不够优雅,这时候还是让CompletableFuture以声明式的方式优雅的处理这些需求
  • Future能干的,CompletableFuture都能干

CompletableFuture对Future的改进

CompletableFuture为什么会出现

  • get()方法在Future计算完成之前会一直处在阻塞状态下,阻塞(低效)的方式和异步编程(追求高效)的设计理念相违背。
  • isDone()方法容易耗费cpu资源(cpu空转),
  • 对于真正的异步处理我们希望是可以通过传入回调函数,在Future结束时自动调用该回调函数,这样就不用空等结果

JDK8设计出CompletableFuture,提供了一种观察者模式类似的机制,可以让任务执行完成后通知监听的一方

CompletableFuture和CompletionStage介绍

类架构说明

在这里插入图片描述

  • 接口CompletionStage
    • 代表异步计算过程中的某一个阶段,一个阶段完成以后可能会触发另外一个阶段(例如代码执行节点->代码完成,发送通知->有异常,抛异常)
    • 一个阶段的计算执行可以是一个Function,Consumer或者Runnable。比如:stage.thenApply(x-> square(x)).thenAccept(x -> System.out.print(x)).thenRun(() -> System.out.println()
    • 一个阶段的执行可能是被单个阶段的完成触发,也可能是由多个阶段一起触发
  • 类CompletableFuture
    • 提供了非常强大的Future的扩展功能,可以帮助我们简化异步编程的复杂性,并且提供了函数式编程的能力,可以通过回调的方式处理计算结果,也提供了转换和组合CompletableFuture的方法
    • 它可能代表一个明确完成的Future,也可能代表一个完成阶段(CompletionStage),它支持在计算完成以后触发一些函数或执行某些动作

创建异步任务的四个静态方法

在这里插入图片描述

对于Executor参数说明:

  • 若没有指定,则使用默认的ForkJoinPoolcommonPool()作为它的线程池执行异步代码
  • 如果指定线程池,则使用我们自定义的或者特别指定的线程池执行异步代码

runAsync

传的是Runnable,所以没有返回值

public static CompletableFuture<Void> runAsync(Runnable runnable);
public static CompletableFuture<Void> runAsync(Runnable runnable,Executor executor);

建议都指定线程池,不然主线程运行完成之后,默认线程池会跟着关闭,这样异步任务就停止执行了

supplyAsync

有返回值

public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier);
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier,Executor executor);

实例

public class CompletableFutureBuildDemo {public static void main(String[] args) throws ExecutionException, InterruptedException {ExecutorService executorService = Executors.newFixedThreadPool(3);// 无返回值CompletableFuture<Void> completableFuture = CompletableFuture.runAsync(() -> {// 不指定线程池,输出类似于ForkJoinPool. commonPool-worker-1System.out.println(Thread.currentThread().getName());try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}},executorService);// 输出nullSystem.out.println(completableFuture.get()); // 有返回值CompletableFuture<String> objectCompletableFuture = CompletableFuture.supplyAsync(()->{System.out.println(Thread.currentThread().getName());try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}return "hello supplyAsync";},executorService);// hello supplyAsyncSystem.out.println(objectCompletableFuture.get());// 关闭线程池executorService.shutdown();}
}

有返回值的方法输出

在这里插入图片描述

任务完成回调+任务异常

CompletableFuture减少阻塞和轮询,可以传入回调对象,当异步任务完成或者发生异常时,自动调用回调对象的回调方法。

public class CompletableFutureUseDemo {public static void main(String[] args) throws ExecutionException, InterruptedException {ExecutorService executorService = Executors.newFixedThreadPool(3);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();}// 模拟产生异常情况if (result > 5) { int i = 10 / 0;}System.out.println("----------1秒钟后出结果" + result);return result;}, executorService).whenComplete((v, e) -> {// v 上一步的result// e 异常if (e == null) {// 没有异常System.out.println("计算完成 获取值" + v);}}).exceptionally(e -> {e.printStackTrace();System.out.println("异常情况:" + e.getCause() + " " + e.getMessage());return null;});System.out.println(Thread.currentThread().getName() + "先去忙其他任务");executorService.shutdown();}
}
/**
无异常情况
pool-1-thread-1---come in
main先去忙其他任务
----------1秒钟后出结果2
计算完成 获取值2
*/
/**
有异常情况
*pool-1-thread-1---come in
main先去忙其他任务
java.util.concurrent.CompletionException: java.lang.ArithmeticException: / by zero
异常情况:java.lang.ArithmeticException: / by zero java.lang.ArithmeticException: / by zero
*/

主线程创建异步任务之后,就继续干自己的事情,异步任务用另外的线程去做事,做完之后可以通知主线程

CompletableFuture优点

  • 异步任务结束时,会自动回调某个对象的方法
  • 主线程设置好回调后,不用关心异步任务的执行,异步任务之间可以顺序执行
  • 异步任务出错时,会自动回调某个对象的方法

案例精讲:从电商网站的比价需求展开

函数式编程已成为主流

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

函数式接口

在这里插入图片描述

在这里插入图片描述

  • Runnable:无参数、无返回值

在这里插入图片描述

  • Function:接受一个参数,并且有一个返回值

在这里插入图片描述

  • Consumer:接受一个参数,没有返回值

在这里插入图片描述

  • BiConsumer:接受两个参数,没有返回值

在这里插入图片描述

两个输入参数,没有返回值。在whenCompelete中,需要放一个泛型来接收返回值,一个异常

在这里插入图片描述

在这里插入图片描述

  • Supplier:没有参数,有一个返回值

在这里插入图片描述

在这里插入图片描述

chain链式调用

public class CompletableFutureMallDemo {public static void main(String[] args) {Student student = new Student();student.setId(1).setStudentName("z3").setMajor("english"); //链式调用}
}@AllArgsConstructor
@NoArgsConstructor
@Data
@Accessors(chain = true)//开启链式调用
class Student {private Integer id;private String studentName;private String major;
}

大厂业务需求说明

切记:功能—>性能(完成—>完美)

电商网站比价需求分析:

1、需求说明:

  1. 同一款产品,同时搜索出同款产品在各大电商平台的售价
  2. 同一款产品,同时搜索出本产品在同一个电商平台下,各个入驻卖家售价是多少

2、输出返回:出来结果希望是同款产品的在不同地方的价格清单列表,返回一个List

  • 《Mysql》 in jd price is 88.05
  • 《Mysql》 in dangdang price is 86.11
  • 《Mysql》 in taobao price is 90.43

3、解决方案,对比同一个产品在各个平台上的价格,要求获得一个清单列表

  1. step by step,按部就班,查完淘宝查京东,查完京东查天猫…
  2. all in,万箭齐发,一口气多线程异步任务同时查询

一波流Java8函数式编程带走-比价案例实战Case

/**
这里面需要注意一下Stream流方法的使用
这种异步查询的方法大大节省了时间消耗,可以融入简历项目中,和面试官有所探讨
*/
public class CompletableFutureMallDemo {static List<NetMall> list = Arrays.asList(new NetMall("jd"), new NetMall("dangdang"), new NetMall("taobao"), new NetMall("dangdang"));/*** step by step* @param list* @param productName* @return*/public static List<String> getPrice(List<NetMall> list, String productName) {//《Mysql》 in jd price is 88.05return list.stream().map(netMall ->String.format("《" + productName + "》" + "in %s price is %.2f",netMall.getNetMallName(),netMall.calcPrice(productName))).collect(Collectors.toList());}/*** all in* 把list里面的内容映射给CompletableFuture()* @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)))) //Stream<CompletableFuture<String>>.collect(Collectors.toList()) //List<CompletableFuture<String>>.stream()//Stream<String>.map(s -> s.join()).collect(Collectors.toList()); //List<String>}public static void main(String[] args) {/*** 采用step by setp方式查询* 《masql》in jd price is 110.23* 《masql》in dangdang price is 109.34* 《masql》in taobao price is 109.31* ------costTime: 3131 毫秒*/long StartTime = System.currentTimeMillis();List<String> list1 = getPrice(list, "masql");for (String element : list1) {System.out.println(element);}long endTime = System.currentTimeMillis();System.out.println("------costTime: " + (endTime - StartTime) + " 毫秒");/*** 采用 all in三个异步线程方式查询* 《mysql》in jd price is 109.31* 《mysql》in dangdang price is 109.67* 《mysql》in taobao price is 110.30* ------costTime1024 毫秒*/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) + " 毫秒");}
}@AllArgsConstructor
@NoArgsConstructor
@Data
// 电商平台
class NetMall {private String netMallName;public double calcPrice(String productName) {try {// 模拟需要一秒查询出来TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}// ThreadLocalRandom.current().nextDouble() 0到1之间的一个数字return ThreadLocalRandom.current().nextDouble() * 2 + productName.charAt(0);}
}

CompletableFuture常用方法

获得结果和触发计算

获取结果getjoingetNow

  • public T get()

  • public T get(long timeout,TimeUnit unit):如system.out.println(completableFuture.get(2L, TimeUnit.SECONDS));

  • public T join() :和get一样,都是获取结果,只是不需要抛出异常

  • public T getNow(T valuelfAbsent) :计算完成就返回正常值,否则返回传入的valueIfAbsent,立即获取结果不阻塞

【源码】

     /*** Returns the result value (or throws any encountered exception)* if completed, else returns the given valueIfAbsent.** @param valueIfAbsent the value to return if not completed* @return the result value, if completed, else the given valueIfAbsent* @throws CancellationException if the computation was cancelled* @throws CompletionException if this future completed* exceptionally or a completion computation threw an exception*/public T getNow(T valueIfAbsent) {Object r;return ((r = result) == null) ? valueIfAbsent : reportJoin(r);}

在这里插入图片描述

join()和get()的区别

join()和get()的作用都是取值,两者的区别是在编译期间是否会做检查型异常处理

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

主动触发计算complete

  • public boolean complete(T value) :是否打断get方法立即返回传入的参数,返回是否打断成功
private static void group1() throws InterruptedException, ExecutionException, TimeoutException {CompletableFuture<String> completableFuture = CompletableFuture.supplyAsync(() -> {//暂停几秒钟线程try {TimeUnit.SECONDS.sleep(2);} catch (InterruptedException e) {e.printStackTrace();}return "abc";});//System.out.println(completableFuture.get());
//        System.out.println(completableFuture.get(1L, TimeUnit.SECONDS));//System.out.println(completableFuture.join());//暂停几秒钟线程try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}//System.out.println(completableFuture.getNow("xxx"));System.out.println(completableFuture.complete("completeValue") + "\t" + completableFuture.get());
}

运行结果如下:

  • 异步任务执行需要两秒,过了一秒就通过join获取结果,本来是要阻塞的,complete方法把这个过程打断了,并将结果值设置为传入的completeValue

在这里插入图片描述

对计算结果进行处理(串行化)

thenApply

  • thenApply:计算结果存在依赖关系,两个线程需要串行化(如先买鱼,再煮鱼),由于存在依赖关系,当前步骤有异常的话就叫停(当前步错,不走下一步)
public class CompletableFutureApiDemo {public static void main(String[] args) throws ExecutionException, InterruptedException, TimeoutException {ExecutorService threadPool = Executors.newFixedThreadPool(3);CompletableFuture<Integer> completableFuture = CompletableFuture.supplyAsync(() -> {try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}return 1;}, threadPool).thenApply(f -> {System.out.println("222");return f + 2;}).thenApply(f -> {System.out.println("3333");return f + 3;}).whenComplete((v, e) -> {if (e == null) {System.out.println("----计算结果" + v);}}).exceptionally(e -> {e.printStackTrace();System.out.println(e.getCause());return null;});System.out.println(Thread.currentThread().getName() + "------主线程先去做其他事情");}
}

在这里插入图片描述

handle

  • handle:计算结果存在依赖关系,两个线程串行化,功能类似于thenApply,但有异常也可以往下走一步

在这里插入图片描述

public class CompletableFutureApiDemo {public static void main(String[] args) throws ExecutionException, InterruptedException, TimeoutException {ExecutorService threadPool = Executors.newFixedThreadPool(3);CompletableFuture<Integer> completableFuture = CompletableFuture.supplyAsync(() -> {try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}return 1;}, threadPool).thenApply(f -> {System.out.println("222");return f + 2;}).handle((f, e) -> {System.out.println("3333");int i=10/0;return f + 2;}).whenComplete((v, e) -> {if (e == null) {System.out.println("----计算结果" + v);}}).exceptionally(e -> {e.printStackTrace();System.out.println(e.getCause());return null;});System.out.println(Thread.currentThread().getName() + "------主线程先去做其他事情");}
}

对计算结果进行消费

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

thenAccept

public class CompletableFutureApi2Demo {public static void main(String[] args) {ExecutorService threadPool = Executors.newFixedThreadPool(3);CompletableFuture.supplyAsync(() -> {return 1;}, threadPool).thenApply(f -> {return f + 2;}).thenApply(f -> {return f + 3;}).thenAccept(r -> {System.out.println(r);//输出6});}
}

thenRun、thenAccept、thenApply对比

  • thenRun(Runnable runnable) :任务A执行完执行B,并且不需要A的结果
  • thenAccept(Consumer action): 任务A执行完执行B,B需要A的结果,但是任务B没有返回值
  • thenApply(Function fn): 任务A执行完执行B,B需要A的结果,同时任务B有返回值

在这里插入图片描述

public class CompletableFutureApi2Demo {public static void main(String[] args) {System.out.println(CompletableFuture.supplyAsync(() -> "result").thenRun(() -> {}).join());//nullSystem.out.println(CompletableFuture.supplyAsync(() -> "result").thenAccept(r -> System.out.println(r)).join());//result nullSystem.out.println(CompletableFuture.supplyAsync(() -> "result").thenApply(f -> f + 2).join());//result2}
}

有无Async对比

  • 如果没有传入自定义线程池,都用默认线程池ForkJoinPool
  • 传入一个线程池,如果你执行第一个任务时,传入了一个自定义线程池
    • 调用thenRun方法执行第二个任务时,则下一个任务和当前任务时共用同一个线程池

在这里插入图片描述

  • 调用thenRunAsync执行第二个任务时,则第一个任务使用的是你自定义的线程池,第二个任务使用的是ForkJoin线程池

在这里插入图片描述

  • 如果第一个任务线程处理太快,系统按照优化切换原则, 直接使用main线程处理,thenAccept和thenAcceptAsync,thenApply和thenApplyAsync等,之间的区别同理。

在这里插入图片描述

源码分析

在这里插入图片描述

在这里插入图片描述

对计算速度进行选用applyToEither

  • 谁快用谁
public class CompletableFutureApiDemo {public static void main(String[] args) {ExecutorService threadPool = Executors.newFixedThreadPool(3);CompletableFuture<String> playA = CompletableFuture.supplyAsync(() -> {try {System.out.println("A come in");TimeUnit.SECONDS.sleep(3);} catch (InterruptedException e) {e.printStackTrace();}return "playA";}, threadPool);CompletableFuture<String> playB = CompletableFuture.supplyAsync(() -> {try {System.out.println("B come in");TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}return "playB";}, threadPool);// A和B相比,谁更快用谁的结果CompletableFuture<String> result = playA.applyToEither(playB, f -> {return f + " is winner";});System.out.println(Thread.currentThread().getName() + "-----------winner:" + result.join());}
}

【输出】

A come in
B come in
main-----------winner:playB is winner

两任务组合

两个任务完成才做下一个

  • runAfterBoth: 组合两个future,不需要获取future的结果,只需两个future处理完任务后,处理任务3。

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

  • thenAcceptBoth: 组合两个future,获取两个future任务的返回结果,然后处理任务3,没有返回值。

在这里插入图片描述

在这里插入图片描述

  • thenCombine: 组合两个future,获取两个future的返回结果,并返回当前任务的返回值

在这里插入图片描述

在这里插入图片描述

对计算结果进行合并thenCombine
  • 两个CompletableStage任务都完成后,最终能把两个任务的结果一起交给thenCombine来处理
  • 先完成的先等着,等待其他分支任务
public class CompletableFutureApi3Demo {public static void main(String[] args) {CompletableFuture<Integer> completableFuture1 = CompletableFuture.supplyAsync(() -> {System.out.println(Thread.currentThread().getName() + " 启动");try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}return 10;});CompletableFuture<Integer> completableFuture2 = CompletableFuture.supplyAsync(() -> {System.out.println(Thread.currentThread().getName() + " 启动");try {TimeUnit.SECONDS.sleep(2);} catch (InterruptedException e) {e.printStackTrace();}return 20;});CompletableFuture<Integer> finalResult = completableFuture1.thenCombine(completableFuture2, (x, y) -> {System.out.println("----------开始两个结果合并");return x + y;});System.out.println(finalResult.join());}
}

输出:30

在这里插入图片描述

A、B中一个完成就做下一个

在这里插入图片描述

  • 接收前面任务的返回值,当前任务有返回值

在这里插入图片描述

  • 接收前面任务的返回值,当前任务无返回值

在这里插入图片描述

  • 不接收前面任务的返回值,当前任务无返回值

在这里插入图片描述

在这里插入图片描述

多任务组合

allOf:等待所有任务完成

// 创建三个CompletableFuture对象,每个代表一个异步任务
CompletableFuture<String> futureImg = CompletableFuture.supplyAsync(() -> {System.out.println("查询商品的图片信息");return "hello.jpg";
});CompletableFuture<String> futureAtrr = CompletableFuture.supplyAsync(() -> {System.out.println("查询商品的属性");return "黑色+256G";
});CompletableFuture<String> futureDesc = CompletableFuture.supplyAsync(() -> {System.out.println("查询商品介绍");return "华为";
});// 使用CompletableFuture.allOf方法等待所有任务完成
CompletableFuture<Void> allOf = CompletableFuture.allOf(futureImg, futureAtrr, futureDesc);
// 等待所有结果完成
allOf.get();
System.out.println("main...end..." + futureImg.get() + ":" + futureAtrr.get() + ":" + futureDesc.get());

如果有很多个相似的任务,不需要像上面一样一个一个异步任务的创建,可以使用数组的方式

CompletableFuture[] futureArr = new CompletableFuture[taskList.size()];
for (int i = 0; i < taskList.size(); i++) {int finalI = i;futureArr[i] = CompletableFuture.runAsync(() -> backUpByTask(taskList.get(finalI)), executor);
}
CompletableFuture.allOf(futureArr).join();

anyOf:只要有一个任务完成

CompletableFuture<Object> anyOf = CompletableFuture.anyOf(futureImg, futureAttr, futureDesc);
// 等待任意一个任务完成,返回第一个完成的任务的返回值
anyOf.get(); 
System.out.println("main...end..." + futureImg.get() + "=>" + futureAttr.get() + "=>" + futureDesc.get());
System.out.println("main...end..." + anyOf.get());

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

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

相关文章

解决nginx代理静态资源刷新后404问题

背景 在公司的项目中&#xff0c;有一个管理系统&#xff0c;大致的逻辑是通过nginx代理的静态资源&#xff0c; 正常页面跳转是没有问题的&#xff0c;有的时候我们会使用回车或者F5进行 页面刷新的时候都会出现404问题。 解决 这种我怀疑是nginx的配置不到位的问题。 我在本…

数据库管理-第218期 服务器内存(20240711)

数据库管理218期 2024-07-11 数据库管理-第218期 服务器内存&#xff08;20240711&#xff09;1 内存2 ECC内存3 原理3.1 多副本传输3.2 纠错码3.3 汉明码 总结 数据库管理-第218期 服务器内存&#xff08;20240711&#xff09; 作者&#xff1a;胖头鱼的鱼缸&#xff08;尹海文…

数据库第六次

视图 salary decimal(10,2) not null default 0 comment ‘工资’, address varchar(200) not null default ‘’ comment ‘通讯地址’, dept_id int comment ‘部门编号’ ); create index idx_name on emp(emp_name); create index idx_birth on emp(birth); create index…

鸿蒙开发:Universal Keystore Kit(密钥管理服务)【密钥删除(ArkTS)】

密钥删除(ArkTS) 为保证数据安全性&#xff0c;当不需要使用该密钥时&#xff0c;应该删除密钥。 开发步骤 以删除HKDF256密钥为例。 确定密钥别名keyAlias&#xff0c;密钥别名最大长度为64字节。初始化密钥属性集。用于删除时指定密钥的属性TAG&#xff0c;比如删除的密钥…

【java】力扣 合并k个升序链表

文章目录 题目链接题目描述思路代码 题目链接 23.合并k个升序链表 题目描述 给你一个链表数组&#xff0c;每个链表都已经按升序排列。 请你将所有链表合并到一个升序链表中&#xff0c;返回合并后的链表 思路 我在这个题里面用到了PriorityQueue(优先队列) 的知识 Prio…

顶顶通呼叫中心中间件实现随时启动和停止质检(mod_cti基于FreeSWITCH)

文章目录 前言联系我们拨号方案启动停止ASR执行FreeSWITCH 命令接口启动ASR接口停止ASR接口 通知配置cti.json配置质检结果写入数据库 前言 顶顶通呼叫中心中间件的实时质检功能是由两个模块组成&#xff1a;mod_asr 和 mod_qc。 mod_asr&#xff1a;负责调用ASR将用户们在通…

进程通信(1):无名管道(pipe)

无名管道(pipe)用来具有亲缘关系的进程之间进行单向通信。半双工的通信方式&#xff0c;数据只能单向流动。 管道以字节流的方式通信&#xff0c;数据格式由用户自行定义。 无名管道多用于父子进程间通信&#xff0c;也可用于其他亲缘关系进程间通信。 因为父进程调用fork函…

开发个人Ollama-Chat--10 绑定域名

开发个人Ollama-Chat–10 绑定域名 域名购买最好找正规的渠道购买&#xff0c;不要因贪图小便宜而多走很多的弯路。我就是第一次购买域名&#xff0c;到了一个坑壁的平台"西部数码"&#xff0c;SSL证书申请了2个月&#xff0c;没下来&#xff0c;客服也贼不专业&…

猫头虎:什么是内耗?

猫头虎 &#x1f42f; 建联猫头虎&#xff0c;商务合作&#xff0c;产品评测&#xff0c;产品推广&#xff0c;个人自媒体创作&#xff0c;超级个体&#xff0c;涨粉秘籍&#xff0c;一起探索编程世界的无限可能&#xff01; 摘要 内耗是指在工作或学习过程中&#xff0c;个…

leetcode日记(37)旋转图像

方法是看评论区想出来的&#xff1a;先将矩阵转置&#xff0c;再将每一行逆转 class Solution { public: int n,m,l,k; struct bian{int u;int v;int d; }; void digui(int loc,int c[],vector<bian> bi,int now,int q,bool colour[],int& maxx,bool jg[]){if(q>…

网络协议 — Keepalived 高可用方案

目录 文章目录 目录Keepalived 是实现了 VRRP 协议的软件Keepalived 的软件架构VRRP StackCheckersKeepalived 的配置Global configurationvrrp_scriptVRRP Configurationvrrp synchroization groupvrrp instancevirtual ip addressesvirtual routesLVS Configurationvirtual_s…

偶数位的数c++

题目描述 给你两个整数 l,r&#xff0c;求 l∼r 范围内有多少个位数为偶数的数。 输入 一行两个整数 l,r。 输出 输出位数为偶数的数的数量。 样例输入 5 15样例输出 6 提示 样例解释 10,11,12,13,14,15 位数为偶数&#xff0c;都是两位数。 数据规模与约定 对于 1…

数据结构之线性表表示集合详解与示例(C,C#,C++)

文章目录 基本特征线性表的特点&#xff1a;线性表的表示方法&#xff1a;C、C#和C语言如何实现一个线性表表示集合1. C实现2. C#实现3. C实现 总结 线性表是计算机数据结构中的一个基本概念&#xff0c;它是一种最简单的抽象数据类型。在线性表中&#xff0c;数据元素之间的关…

Qt进阶版五子棋

五子棋是一种两人对弈的棋类游戏&#xff0c;目标是在横、竖、斜任意方向上连成五个子。在Qt中实现五子棋程序&#xff0c;你需要设计棋盘界面、处理下棋逻辑、判断胜负等。以下是实现一个基本五子棋程序的步骤&#xff1a; 创建项目和界面 使用Qt Creator创建一个新的Qt Widge…

academic-homepage:快速搭建个人学术主页,页面内容包括个人简介、教育经历、发布过的学术列表等,同时页面布局兼容移动端。

今天给大家分享GitHub 上一个开源的 GitHub Pages 模板 academic-homepage。 可帮助你快速搭建个人学术主页&#xff0c;页面内容包括个人简介、教育经历、发布过的学术列表等最基本内容&#xff0c;同时页面布局兼容移动端。 相关链接 github.com/luost26/academic-homepage …

Java语言程序设计——篇四(1)

类和对象 面向对象概述面向过程与面向对象面向对象基本概念面向对象的基本特征面向对象的优势及应用 为对象定义类类的修饰符成员变量成员变量-修饰符 构造方法⭐️成员方法成员方法-修饰符例题讲解 ⚠️理解栈和堆 面向对象概述 两种程序设计方法 结构化程序设计&#xff0c…

Linux RTL8111/RTL8168 不能联网 / 最新版驱动下载安装

注&#xff1a; 机翻&#xff0c;未校对。 如何让 Realtek RTL8111/RTL8168 在 Linux 下工作 这篇文章于 2016 年 8 月在我原来的博客上发布。尽管如今 Linux 下的 RTL8111/RTL8168 网络接口的情况变得越来越稳定&#xff0c;但它们仍然会导致数据包丢失或网络连接不稳定等问题…

Python统计实战:时间序列分析之Winters指数平滑法预测

为了解决特定问题而进行的学习是提高效率的最佳途径。这种方法能够使我们专注于最相关的知识和技能&#xff0c;从而更快地掌握解决问题所需的能力。 &#xff08;以下练习题来源于《统计学—基于Python》。请在Q群455547227下载原始数据。&#xff09; 练习题 下表是某地区2…

C到C嘎嘎的衔接篇

本篇文章&#xff0c;是帮助大家从C向C嘎嘎的过渡&#xff0c;那么我们直接开始吧 不知道大家是否有这样一个问题&#xff0c;学完C的时候感觉还能听懂&#xff0c;但是听C嘎嘎感觉就有点难度或者说很难听懂&#xff0c;那么本篇文章就是帮助大家从C过渡到C嘎嘎。 C嘎嘎与C的区…

企业级移动门户平台是什么?

企业级移动门户平台是为企业提供移动应用和服务的一站式平台。它将企业内部的应用程序和数据集成在一个统一的界面中&#xff0c;方便员工使用&#xff0c;提高工作效率和增强企业的数字化转型能力。WorkPlus作为一种企业级移动门户平台&#xff0c;为企业提供了全面的移动应用…