Java 8 的异步利器:CompletableFuture源码解析(建议精读)

2023-05-27 0 804

completableFuture 是JDK1.8版新导入的类。上面是那个类:

Java 8 的异步利器:CompletableFuture源码解析(建议精读)

同时实现了俩USB,这类是个class。那个是Future的同时实现类,采用 completionStageUSB去全力支持顺利完成时促发的表达式和操作方式。

两个 completetableFuture 就代表者了两个各项任务,他能用Future的方式,还能做许多以后说的 executorService 相互配合 futures 做得好的。

以后future须要等候isDone为true就能晓得各项任务跑完了,或是是用get方式初始化的这时候会再次出现堵塞,而采用completableFuture的采用就能用then,when之类操作方式来避免以内的堵塞和HTTPisDone的现像出现。

1.建立CompletableFuture间接new第一类。

两个 completableFuture 第一类代表者着两个各项任务,那个第一类能跟那个各项任务造成联络。

上面用的 complete方式原意是那个各项任务顺利完成了须要返回的结果,然后用get()

Java 8 的异步利器:CompletableFuture源码解析(建议精读)

2.JDK1.8采用的USB类。

在本文的 CompletableFuture 中大量地采用了这些表达式式USB。

注:这些声明大量应用于方式的入参中,像 thenApplythenAccept 这俩是两个用Function两个用Consumer

而lambda表达式正好是能作为这些USB的同时实现。例如 s->{return 1;}那个就相当于两个Function。因为有入参和返回结果。

Java 8 的异步利器:CompletableFuture源码解析(建议精读)

(1)Function

Java 8 的异步利器:CompletableFuture源码解析(建议精读)

(2)Consumer

Java 8 的异步利器:CompletableFuture源码解析(建议精读)

对于前面有Bi的是这样的,BiConsumer是两个参数的。

Java 8 的异步利器:CompletableFuture源码解析(建议精读)

(3)Predicate那个USB声明是两个入参,返回两个boolean。

Java 8 的异步利器:CompletableFuture源码解析(建议精读)

(4)supplier

Java 8 的异步利器:CompletableFuture源码解析(建议精读)

3.上面是那个类的静态方式

带有Async是促发器执行的原意、也是两个 completableFuture 第一类代表者着两个各项任务那个原则。

这种促发器方式都能指定两个线程池作为各项任务的运行环境,如果没有指定就会采用ForkJoinPool 线程池来执行

Java 8 的异步利器:CompletableFuture源码解析(建议精读)

(1) supplyAsync&runAsync 的采用例子。

publicstatic void main(String[] args) throws ExecutionException, InterruptedException { ExecutorService executorService = Executors.newCachedThreadPool(); executorService.submit(new Callable<Object>() {@Override publicObject call() throws Exception { System.out.println(“executorService 是否为守护线程 :”+ Thread.currentThread().isDaemon());return null; } }); finalCompletableFuture<String> completableFuture = CompletableFuture.supplyAsync(() -> { System.out.println(“this is lambda supplyAsync”); System.out.println(“supplyAsync 是否为守护线程 “ + Thread.currentThread().isDaemon()); try { TimeUnit.SECONDS.sleep(2); } catch(InterruptedException e) { e.printStackTrace(); } System.out.println(“this lambda is executed by forkJoinPool”); return “result1”; }); finalCompletableFuture<String> future = CompletableFuture.supplyAsync(() -> { System.out.println(“this is task with executor”); System.out.println(“supplyAsync 采用executorService 时是否为守护线程 : “ + Thread.currentThread().isDaemon()); return “result2”; }, executorService); System.out.println(completableFuture.get()); System.out.println(future.get()); executorService.shutdown(); }
Java 8 的异步利器:CompletableFuture源码解析(建议精读)

这些各项任务中带有supply是持有返回值的,run是void返回值的,在玩supply时发现两个问题:如果使用supplyAsync各项任务时不采用各项任务的返回值,即不用get方式堵塞主线程会导致各项任务执行中断。

注:跟get方式无关,后面有答案

Java 8 的异步利器:CompletableFuture源码解析(建议精读)
Java 8 的异步利器:CompletableFuture源码解析(建议精读)

然后我开始探索是否是只有 supplyAsync 是这样。我测试了 runAsync 发现也是这样。

Java 8 的异步利器:CompletableFuture源码解析(建议精读)

下图为与 supplyAsync 各项任务执行不全面一样的问题,我甚至测试了将lambda换成runnable发现无济于事。

Java 8 的异步利器:CompletableFuture源码解析(建议精读)

答案:

造成那个原因是因为Daemon。因为 completableFuture 这套采用促发器各项任务的操作方式都是建立成了守护线程,那么我们没有初始化get方式不堵塞那个主线程的这时候。主线程执行完毕,所有线程执行完毕就会导致两个问题,是守护线程退出。

那么我们没有执行的代码是因为主线程不再跑各项任务而关闭导致的,可能那个不叫问题,因为在开发中我们主线程常常是一直开着的。但是那个小问题同样让我想了好久。

上面我们开两个非守护线程,能看到程序执行顺利。

Java 8 的异步利器:CompletableFuture源码解析(建议精读)

上面证实守护线程在其他非守护线程全部退出的情况下不继续执行。

final CompletableFuture<String> completableFuture = CompletableFuture.supplyAsync(() -> { System.out.println(“this is lambda supplyAsync”); System.out.println(“supplyAsync 是否为守护线程 “ + Thread.currentThread().isDaemon()); try { TimeUnit.SECONDS.sleep(1); try(BufferedWriter writer =new BufferedWriter (new OutputStreamWriter(new FileOutputStream(new File(“/Users/zhangyong/Desktop/temp/out.txt”))))){ writer.write(“this is completableFuture daemon test”); }catch(Exception e){ System.out.println(“exception find”); } } catch(InterruptedException e) { e.printStackTrace(); } System.out.println(“this lambda is executed by forkJoinPool”); return “result1”; });

那个代码是操作方式本地文件,并且sleep了一秒。其他线程就一句控制台输出的代码,最终的结果是文件没有任何变化。

当我把主线程 sleep 5 秒时,本地文件会写入一句 this is completableFuture daemon test 验证成功。

(2)allOf&anyOf

这两个方式的入参是两个 completableFuture组、allOf是所有各项任务都顺利完成时返回,但是是个Void的返回值。

anyOf是当入参的 completableFuture 组中有两个各项任务执行完毕就返回,返回结果是第两个顺利完成的各项任务的结果。

publicstatic void otherStaticMethod() throws ExecutionException, InterruptedException {finalCompletableFuture<String> futureOne = CompletableFuture.supplyAsync(() -> {try { Thread.sleep(3000); } catch(InterruptedException e) { System.out.println(“futureOne InterruptedException”); } return “futureOneResult”; }); final CompletableFuture<String> futureTwo = CompletableFuture.supplyAsync(() -> { try { Thread.sleep(6000); } catch (InterruptedException e) { System.out.println(“futureTwo InterruptedException”); }return “futureTwoResult”; }); CompletableFuture future = CompletableFuture.allOf(futureOne, futureTwo); System.out.println(future.get()); // CompletableFuture completableFuture = CompletableFuture.anyOf(futureOne, futureTwo); // System.out.println(completableFuture.get()); }
Java 8 的异步利器:CompletableFuture源码解析(建议精读)
Java 8 的异步利器:CompletableFuture源码解析(建议精读)

(3) completedFuture 那个方式我没懂他是干啥的,源代码是返回两个值。感觉没啥意义。

Java 8 的异步利器:CompletableFuture源码解析(建议精读)

(4)取值方式,除了get还有两个 getNow(); 那个就比较特殊了。

那个方式是执行那个方式的这时候各项任务执行完了就返回各项任务的结果,如果各项任务没有执行完就返回你的入参。

Java 8 的异步利器:CompletableFuture源码解析(建议精读)

(5)join方式跟线程的join用法差不多。

Java 8 的异步利器:CompletableFuture源码解析(建议精读)

(6) whenXXX ,在两个各项任务执行顺利完成之后初始化的方式。

那个有三个名差不多的方式: whenCompletewhenCompleteAsync 、还有两个是 whenCompleteAsync 用自定义 Executor

Java 8 的异步利器:CompletableFuture源码解析(建议精读)

首先看一下那个 whenComplete实例方式。那个是各项任务执行完毕初始化的,传入两个action,那个方式的执行线程是当前线程,意味着会堵塞当前线程。

上面图中test的输出跟 whenComplete 方式运行的线程有关,运行到main线程就会堵塞test的输出,运行的是 completableFuture线程则不会堵塞住test的输出。

Java 8 的异步利器:CompletableFuture源码解析(建议精读)

上面是各项任务执行的线程的探索。

Java 8 的异步利器:CompletableFuture源码解析(建议精读)
Java 8 的异步利器:CompletableFuture源码解析(建议精读)

根据测试得出的结论是:如果初始化 whenComplete 的中途,还发生了其他事情,图中的主线程的 sleep(400); 导致 completableFuture 那个各项任务执行完毕了,那么就采用主线程初始化。

如果初始化的中途没有发生其他各项任务且在触碰到 whenComplete 方式时 completableFuture 那个各项任务还没有彻底执行完毕那么就会用 completableFuture 那个各项任务所采用的线程。

上面是 whenCompleteAsync 方式。那个方式是新建立两个促发器线程执行。所以不会堵塞。

Java 8 的异步利器:CompletableFuture源码解析(建议精读)

(7) then方式瞅着挺多的,实际上是异不促发器和加不加自定义Executor

Java 8 的异步利器:CompletableFuture源码解析(建议精读)

注: whenComplete 中再次出现的问题在then中测试不存在、采用的是上两个各项任务的线程。那个 thenCompose是两个各项任务执行完之后能用它的返回结果接着执行的方式,方法返回的是另两个你期盼泛型的结果。

compose 理解是上两个各项任务结果是then的一部分。

Java 8 的异步利器:CompletableFuture源码解析(建议精读)

上面介绍一下 thenCombine

那个 combine 的理解是结合两个各项任务的结果。

Java 8 的异步利器:CompletableFuture源码解析(建议精读)

综上:那个线程的问题并不是大问题,只要你不用线程来做判断条件,他并不会影响你的效率。试想pool线程都执行完了就用主线程跑呗。没跑完,而使你等了那你就用pool线程呗。

thenRun是那个各项任务运行完,再运行下两个各项任务,感觉像是join了一下。

Java 8 的异步利器:CompletableFuture源码解析(建议精读)

其余不再介绍,大同小异。

thenApply(Function); 这样的是有入参有返回值类型的。

thenAccept(Consumer);这样的是有入参,但是没有返回值的。详情在上文中有过关于表达式式USB的叙述。

相关文章

发表评论
暂无评论
官方客服团队

为您解决烦忧 - 24小时在线 专业服务