有了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";}
}
优缺点分析
优点
- 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");
- 使用 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();
缺点
- 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());
- get容易导致阻塞,一般建议放在程序后面,一旦调用不见不散,非要等到结果才会离开,不管你是否计算完成,容易程序堵塞。
System.out.println(futureTask.get());System.out.println(Thread.currentThread().getName() + "\t ----忙其它任务了");
- 假如我不愿意等待很长时间,我希望过时不候,可以自动离开.
System.out.println(Thread.currentThread().getName() + "\t ----忙其它任务了");System.out.println(futureTask.get(3,TimeUnit.SECONDS));
- 超过 3 秒结束线程抛出 TimeOutException
- 轮询耗费 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("正在处理中,不要再催了,越催越慢 ,再催熄火");}}
小结
- get(),一旦调用 get 方法求结果,如果计算没有完成,容易导致线程阻塞
- isDone()轮询
- 轮询的方式会消耗无谓的 CPU 资源,而且也不见得能及时得到计算结果
- 如果想要异步获取,通常都会以轮询的方式去获取结果,尽量不使用阻塞
- Future 对于结果的获取不是很友好,只能通过阻塞或轮询的方式得到结果
面对一些复杂的任务
对于简单的业务场景使用 Future 接口完全 OK
回调通知
- 应对 Future 的完成时间,完成之后发起回调通知
- 通过轮询方式去判断任务是否完成,非常不优雅,也占用 CPU
创建异步任务
- Future+线程池配合
多个任务前后依赖可以组合
- 若想将多个异步任务的计算结果组合起来,则后一个异步任务的计算结果,需要前一个异步任务的值
- 将两个或多个异步计算合成一个异步计算,这几个异步计算,互相独立,同时后面这个又依赖于前一个处理的结果
对计算速度选最快
- 当 Future 集合中某个任务最快结束时,返回结果,返回第一名处理结果
CompletableFuture 应运而生
- 使用 Future 接口提供的 API,处理不够优雅
- CompletableFuture 以声明式方式优雅的处理这些需求同时规避 Future 自身获取计算结果的弊端
CompletableFuture 对 Future 的改进
CompletableFuture 为什么会出现?
- get()方法在 Future 计算完成之前会一直处于阻塞状态下
- isDone()方法容易耗费 CPU 资源
- 对于真正在异步处理中我们希望可以通过传入回调函数,在 Future 结束时自动回调该函数,这样就不需要等待结果
- 阻塞的方式和异步编程的设计理念相违背,而轮询的方式会耗费无谓的 CPU 资源
- 因此,JDK8 设计出 CompletableFuture
- CompletableFuture 提供了一种与观察者模式类似的机制,可以让任务执行完成后通知监听的一方
- CompletableFuture 提供了一种与观察者模式类似的机制,可以让任务执行完成后通知监听的一方
CompletableFuture 与 CompletionStage 源码
类继承架构
public class CompletableFuture<T> implements Future<T>, CompletionStage<T>{}
接口 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()方式创建异步任务
- 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());
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();
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());
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();
关于参数 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());
- 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();}
- 解释下为什么默认线程池关闭,自定义线程池记得关闭?
- 用户线程中,程序执行完成需要 1 秒钟,main 线程执行太快,在 ForkJoinPool 线程池中若发现 main 线程执行完成则会关闭线程池
- 解决方法
- 将 main 线程延迟 1 秒,在用户线程的 try/catch/finally 代码之后添加睡眠代码
//主线程不要立刻结束,否则CompletableFuture默认使用的线程池会立刻关闭:暂停3秒钟线程try {TimeUnit.SECONDS.sleep(3);} catch (InterruptedException e) {e.printStackTrace();}
- 或是使用定义的线程对象,或是自定义线程对象
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();}
- 编写异常代码测试
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;});}
电商网站比价需求案例
函数式编程已经主流
- 大厂面试题
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 方法中进行调用
Supplier
- 供给型函数式接口,没有参数,有一个返回值
@FunctionalInterface
public interface Supplier<T> {/*** Gets a result.** @return a result*/T get();
}
Summer
先说说 join 和 get 的对比
- get 在程序编译时会检查异常,join 在程序编译时不会检查异常,此外于 get 基本等价
CompletableFuture<String> supplyAsync = CompletableFuture.supplyAsync(() -> {return "join and get";});System.out.println(supplyAsync.join());
- 说说你过去工作中的项目亮点!!!(面试必备)
大厂业务需求说明
- 切记,先完成功能再到性能的逐步迭代
- 电商网站比价需求分析
案例说明:电商比价需求,模拟如下情况: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) + " 毫秒");}
方案二,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());}
- 两次流式映射
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) + " 毫秒");
}
效果比较
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());}
get(long time,TimeUnit unit)
System.out.println(completableFuture.get(2L,TimeUnit.SECONDS));
join()
System.out.println(completableFuture.join());
getNow(String valueIfAbsent)
System.out.println(completableFuture.getNow("xxx"));
- 源码解读
- 当调用 getNow 时,计算完成,获取计算结果
- 当调用 getNow 时,计算未完成,返回备选值(valueIfabsent)
- 异步任务执行 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"));
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());
- 源码说明
- 主线程调用异步任务时
- 计算未完成,返回 true,同时将 value 作为 result 给到主线程
- 计算完成,返回 false,同时将异步任务的计算结果给到主线程
- 将异步任务与主线程的睡眠时间互换,得到以下结果
对计算结果进行处理
- 此处 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();}
- f=1+2=3
- 异常处理
- 计算过程中出现异常,thenApply(),会直接终止计算
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();}
- 异常处理
- 有异常时,跳过异常代码,带着异常参数继续执行后续代码
Summary
对计算结果进行消费
- 接受任务的处理结果,并消费处理,无返回结果
demo
- 源码解读
- 调用了 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);}
补充
- thenRun 不调用前置计算的结果
- thenAccpet 获取前置计算结果,最终不返回记过,consumer 直接消费
- thenApply,获取前置计算结果,最终返回所有计算结果
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();}}
- 将所有的方法都统一为 thenRun
- 将入口睡眠代码注释
- 结论
- 没有传入自定义线程池,都默认使用 ForkJoinPool
- 传入一个指定线程池之后
- 执行第一个任务时,传入指定线程池
- 调用 thenRun 方法执行第二个任务时,则第一个任务和第二个任务共用同一个线程池
- 调用 thenRunAsync 执行第二个任务时,则第一个任务用指定线程池,第二个任务用 ForkJoinPool
- 执行第一个任务时,传入指定线程池
- 有可能处理太快,系统优化切换原则直接使用 main 线程处理
- 其它 thenAccept 与 thenAccpetAsync,thenApply 和 thenApplyAsync 等,之间的区别亦是同理
- 源码解读
- 调用 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());}
对计算结果进行合并
- 两个 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());}
- 函数式接口方式
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());}
相关文章:

有了Future为什么还要CompletableFuture?
文章目录 Future 接口理论知识复习Future 接口概述场景描述小结 Future 接口常用实现类 FutureTask 异步任务Future 的作用Futrue 编码测试优缺点分析优点缺点小结 面对一些复杂的任务对于简单的业务场景使用 Future 接口完全 OK回调通知创建异步任务多个任务前后依赖可以组合对…...

Android super.img解包和打包指南(含工具下载lpunpack、lpmake、lpdump)
本文所有命令均需要在linux 上执行 一、解包 1、将Android sparse image格式的super.img转成二进制文件 $ sudo apt install android-sdk-libsparse-utils $ simg2img super.img super.img.bin 2、下载工具lpunpack 和lpmake、lpdump 以及其依赖库 下载地址:https://downl…...

端到端实现高精地图重建(TopoNet解读和横评)
论文出处 [2304.05277] Graph-based Topology Reasoning for Driving Scenes (arxiv.org)https://arxiv.org/abs/2304.05277 TopoNet TopoNet的目标是从车辆上安装的多视角摄像头获取图像,感知实体并推理出驾驶场景的拓扑关系,实现端到端预测…...

系统架构20 - 统一建模语言UML(上)
统一建模语言 组成要素事物关系 在目前的软件开发方法中,面向对象的方法占据着主导地位。面向对象方法的主导地位也决定着软件开发过程模型化技术的发展,面向对象的建模技术方法也就成为主导的方法。 公认的面向对象建模语言出现于20世纪70年代中期。从1…...

数据库学习笔记2024/2/4
随笔 1. 为什么学? 认识数据,熟悉数据,掌握数据。 进企业必备技能。 2. 怎么学? 1、MySQL数据库就是存储和管理数据的一个大型软件,这个软件有一个专门的语言叫SQL,主要学的是SQL语言,但想要达到企业用人标准,就还得学会熟练使用MySQL这个软件。 2、学习分三阶段: 一. …...

Apache POI 处理excel文件 记录用法
Apache POI 写excel public static void write() throws IOException {//再内存中创建了一个Excel文件XSSFWorkbook excel new XSSFWorkbook();//创建一个sheet页XSSFSheet sheet excel.createSheet("info");//这里创建行对象,这里的rownum 是从0开始的,类似于数…...

Transformer实战-系列教程2:Transformer算法解读2
🚩🚩🚩Transformer实战-系列教程总目录 有任何问题欢迎在下面留言 Transformer实战-系列教程1:Transformer算法解读1 Transformer实战-系列教程2:Transformer算法解读2 5、Multi-head机制 在4中我们的输入是X&#x…...

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

STM32 IIC电量计LTC2944
1 描述 LTC2944 可在便携式产品应用中测量电池充电状态、电池电压、电池电流及其自身温度。宽输入电压范围允许使用高达 60V 的多节电池。精密库仑反向积分电流通过电池正极端子与负载或充电器之间的检测电阻器。 电压、电流和温度由内部 14 位无延迟 ΔΣ™ ADC 测量。测量结…...
Linux 链接 GitHub 出现 Connection timed out
问题 安装GIT并完成公钥验证:Linux 系统拉取 Github项目 [rootxxx devtools]# ssh -T gitgithub.com ssh: connect to host github.com port 22: Connection timed out解决方案 进入在存放公钥私钥id_rsa.pub文件里,新建/修改config文本 [rootxxx my…...

vulnhub靶场之Thales
一.环境搭建 1.靶场描述 Description : Open your eyes and change your perspective includes 2 flags:user.txt and root.txt. Telegram: machineboy141 (for any hint) This works better with VIrtualBox rathe than VMware 2.靶场地址 https://www.vulnhub.com/entry/t…...

Qt之使用Qt内置图标
一效果 二.原理 Qt内置图标封装在QStyle中,共七十多个图标,可以直接拿来用,能应付不少简单程序需求,不用自己去找图标并添加到资源文件了。 下面是内置图标的枚举定义: enum StandardPixmap {SP_TitleBarMenuButton,SP_TitleBarMinButton,SP_TitleBarMaxButton,SP_T…...

《计算机网络简易速速上手小册》第10章:未来网络技术趋势(2024 最新版)
文章目录 10.1 边缘计算与网络设计 - 未来网络的速度与激情10.1.1 基础知识10.1.2 重点案例:使用 Python 实现边缘计算的实时视频分析准备工作Python 脚本示例 10.1.3 拓展案例1:智能交通系统Python 脚本示例 - 边缘计算设备上的交通流量分析 10.1.4 拓展…...

Vue引入Axios
1.命令安装axios和vue-axios npm install axios --save npm install vue-axios --save 2.package.json查看版本 3.在main.js中引用 import axios from axios; import VueAxios from vue-axios; Vue.use(VueAxios,axios) 4.如何使用 (初始化方法) 将下列代…...

【git 本地管理版本及与github合并】 Init Push Pull操作解决方案
文章目录 创建本地仓库,并与远程仓库链接更新本地仓库并使用Push推送到远程仓库 1. 几种基础命令介绍:2. git push操作流程 .gitignore删除本地仓库,断开本地与远程的链接设置用于提交commit的用户名,邮箱,以便githu…...

JavaSE-项目小结-IP归属地查询(本地IP地址库)
一、项目介绍 1. 背景 IP地址是网络通信中的重要标识,通过分析IP地址的归属地信息,可以帮助我们了解访问来源、用户行为和网络安全等关键信息。例如应用于网站访问日志分析:通过分析访问日志中的IP地址,了解网站访问者的地理位置分…...

使用最大边界相关算法处理文章自动摘要
一、需求背景 对于博客或者文章来说,摘要是普遍性的需求。但是我们不可能让作者自己手动填写摘要或者直接暴力截取文章的部分段落作为摘要,这样既不符合逻辑又不具有代表性,那么,是否有相关的算法或者数学理论能够完成这个需求呢&…...

ref和reactive, toRefs的使用
看尤雨溪说:为什么Vue3 中应该使用 Ref 而不是 Reactive? toRefs import { ref, toRefs } from vue;// 定义一个响应式对象 const state ref({count: 0,name: Vue });// 使用toRefs转换为响应式引用对象 const reactiveState toRefs(state);// 现在你…...

从源代码看Chrome 版本号
一直以来都是用Chrome 浏览器,但是看到Chrome 点分4 组数据的表达方式,总是感觉怪怪的,遂深入源代码了解她的版本号具体表示的内容 chrome 浏览器中显示的版本号 源代码中的版本号标识 版本号文件位于 chrome/VERSION , 看到源代…...
Vue 图片加载失败处理
Vue 图片加载失败处理 很多人会使用 error 方法在图片加载 失败时替换img.src 的方式 但是这种方式在默认图片加载失败时,error会出现死循环,所以我使用了error v-if的方式。 <template><div><!-- 正常时显示 --><img v-if&quo…...

wordpress后台更新后 前端没变化的解决方法
使用siteground主机的wordpress网站,会出现更新了网站内容和修改了php模板文件、js文件、css文件、图片文件后,网站没有变化的情况。 不熟悉siteground主机的新手,遇到这个问题,就很抓狂,明明是哪都没操作错误&#x…...

Chapter03-Authentication vulnerabilities
文章目录 1. 身份验证简介1.1 What is authentication1.2 difference between authentication and authorization1.3 身份验证机制失效的原因1.4 身份验证机制失效的影响 2. 基于登录功能的漏洞2.1 密码爆破2.2 用户名枚举2.3 有缺陷的暴力破解防护2.3.1 如果用户登录尝试失败次…...
OpenLayers 可视化之热力图
注:当前使用的是 ol 5.3.0 版本,天地图使用的key请到天地图官网申请,并替换为自己的key 热力图(Heatmap)又叫热点图,是一种通过特殊高亮显示事物密度分布、变化趋势的数据可视化技术。采用颜色的深浅来显示…...
SciencePlots——绘制论文中的图片
文章目录 安装一、风格二、1 资源 安装 # 安装最新版 pip install githttps://github.com/garrettj403/SciencePlots.git# 安装稳定版 pip install SciencePlots一、风格 简单好用的深度学习论文绘图专用工具包–Science Plot 二、 1 资源 论文绘图神器来了:一行…...
k8s从入门到放弃之Ingress七层负载
k8s从入门到放弃之Ingress七层负载 在Kubernetes(简称K8s)中,Ingress是一个API对象,它允许你定义如何从集群外部访问集群内部的服务。Ingress可以提供负载均衡、SSL终结和基于名称的虚拟主机等功能。通过Ingress,你可…...

Vue3 + Element Plus + TypeScript中el-transfer穿梭框组件使用详解及示例
使用详解 Element Plus 的 el-transfer 组件是一个强大的穿梭框组件,常用于在两个集合之间进行数据转移,如权限分配、数据选择等场景。下面我将详细介绍其用法并提供一个完整示例。 核心特性与用法 基本属性 v-model:绑定右侧列表的值&…...

[10-3]软件I2C读写MPU6050 江协科技学习笔记(16个知识点)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16...
如何为服务器生成TLS证书
TLS(Transport Layer Security)证书是确保网络通信安全的重要手段,它通过加密技术保护传输的数据不被窃听和篡改。在服务器上配置TLS证书,可以使用户通过HTTPS协议安全地访问您的网站。本文将详细介绍如何在服务器上生成一个TLS证…...

多模态大语言模型arxiv论文略读(108)
CROME: Cross-Modal Adapters for Efficient Multimodal LLM ➡️ 论文标题:CROME: Cross-Modal Adapters for Efficient Multimodal LLM ➡️ 论文作者:Sayna Ebrahimi, Sercan O. Arik, Tejas Nama, Tomas Pfister ➡️ 研究机构: Google Cloud AI Re…...
ip子接口配置及删除
配置永久生效的子接口,2个IP 都可以登录你这一台服务器。重启不失效。 永久的 [应用] vi /etc/sysconfig/network-scripts/ifcfg-eth0修改文件内内容 TYPE"Ethernet" BOOTPROTO"none" NAME"eth0" DEVICE"eth0" ONBOOT&q…...