0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

JDK1.8使用的接口类

Android编程精选 来源:Java知音 作者:Java知音 2022-07-26 11:09 次阅读

completableFuture是JDK1.8版本新引入的类。下面是这个类:

38111d08-0c0f-11ed-ba43-dac502259ad0.png

实现了俩接口,本身是个class。这个是Future的实现类,使用completionStage接口去支持完成时触发的函数和操作。

一个completetableFuture就代表了一个任务,他能用Future的方法,还能做一些之前说的executorService配合futures做不了的。

之前future需要等待isDone为true才能知道任务跑完了,或者就是用get方法调用的时候会出现阻塞,而使用completableFuture的使用就可以用then,when等等操作来防止以上的阻塞和轮询isDone的现象出现。

1.创建CompletableFuture直接new对象。

一个completableFuture对象代表着一个任务,这个对象能跟这个任务产生联系。

下面用的complete方法意思就是这个任务完成了需要返回的结果,然后用get()方法可以获取到。

38221c20-0c0f-11ed-ba43-dac502259ad0.png

2.JDK1.8使用的接口类。

在本文的CompletableFuture中大量的使用了这些函数式接口。

注:这些声明大量应用于方法的入参中,像thenApply和thenAccept这俩就是一个用Function一个用Consumer

而lambda函数正好是可以作为这些接口的实现。例如 s->{return 1;} 这个就相当于一个Function。因为有入参和返回结果。

3832c4d0-0c0f-11ed-ba43-dac502259ad0.png(1)Function

383e5a5c-0c0f-11ed-ba43-dac502259ad0.png(2)Consumer

384e7982-0c0f-11ed-ba43-dac502259ad0.png对于前面有Bi的就是这样的,BiConsumer就是两个参数的。

385bbea8-0c0f-11ed-ba43-dac502259ad0.png(3)Predicate这个接口声明是一个入参,返回一个boolean。

38677144-0c0f-11ed-ba43-dac502259ad0.png(4)supplier

38776fcc-0c0f-11ed-ba43-dac502259ad0.png

3.下面是这个类的静态方法

带有Async就是异步执行的意思、也是一个completableFuture对象代表着一个任务这个原则。

这种异步方法都可以指定一个线程池作为任务的运行环境,如果没有指定就会使用ForkJoinPool线程池来执行

3885b528-0c0f-11ed-ba43-dac502259ad0.png(1)supplyAsync&runAsync的使用例子。

publicstaticvoidmain(String[]args)throwsExecutionException,InterruptedException{
ExecutorServiceexecutorService=Executors.newCachedThreadPool();
executorService.submit(newCallable(){
@Override
publicObjectcall()throwsException{
System.out.println("executorService是否为守护线程:"+Thread.currentThread().isDaemon());
returnnull;
}
});
finalCompletableFuturecompletableFuture=CompletableFuture.supplyAsync(()->{
System.out.println("thisislambdasupplyAsync");
System.out.println("supplyAsync是否为守护线程"+Thread.currentThread().isDaemon());
try{
TimeUnit.SECONDS.sleep(2);
}catch(InterruptedExceptione){
e.printStackTrace();
}
System.out.println("thislambdaisexecutedbyforkJoinPool");
return"result1";
});
finalCompletableFuturefuture=CompletableFuture.supplyAsync(()->{
System.out.println("thisistaskwithexecutor");
System.out.println("supplyAsync使用executorService时是否为守护线程:"+Thread.currentThread().isDaemon());
return"result2";
},executorService);
System.out.println(completableFuture.get());
System.out.println(future.get());
executorService.shutdown();
}

389f349e-0c0f-11ed-ba43-dac502259ad0.png这些任务中带有supply是持有返回值的,run是void返回值的,在玩supply时发现一个问题:如果使用supplyAsync任务时不使用任务的返回值,即不用get方法阻塞主线程会导致任务执行中断。

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

38b40db0-0c0f-11ed-ba43-dac502259ad0.png38c84e88-0c0f-11ed-ba43-dac502259ad0.png然后我开始探索是否是只有supplyAsync是这样。我测试了runAsync发现也是这样。

38d6c0b2-0c0f-11ed-ba43-dac502259ad0.png下图为与supplyAsync任务执行不全面一样的问题,我甚至测试了将lambda换成runnable发现无济于事。

38f16818-0c0f-11ed-ba43-dac502259ad0.png

答案:

造成这个原因是因为Daemon。因为completableFuture这套使用异步任务的操作都是创建成了守护线程,那么我们没有调用get方法不阻塞这个主线程的时候。主线程执行完毕,所有线程执行完毕就会导致一个问题,就是守护线程退出。

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

下面我们开一个非守护线程,可以看到程序执行顺利。

3900667e-0c0f-11ed-ba43-dac502259ad0.png下面证实守护线程在其他非守护线程全部退出的情况下不继续执行。

finalCompletableFuturecompletableFuture=CompletableFuture.supplyAsync(()->{
System.out.println("thisislambdasupplyAsync");
System.out.println("supplyAsync是否为守护线程"+Thread.currentThread().isDaemon());
try{
TimeUnit.SECONDS.sleep(1);
try(BufferedWriterwriter=newBufferedWriter
(newOutputStreamWriter(newFileOutputStream(newFile("/Users/zhangyong/Desktop/temp/out.txt"))))){
writer.write("thisiscompletableFuturedaemontest");
}catch(Exceptione){
System.out.println("exceptionfind");
}
}catch(InterruptedExceptione){
e.printStackTrace();
}
System.out.println("thislambdaisexecutedbyforkJoinPool");
return"result1";
});

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

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

(2)allOf&anyOf

这两个方法的入参是一个completableFuture组、allOf就是所有任务都完成时返回,但是是个Void的返回值。

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

publicstaticvoidotherStaticMethod()throwsExecutionException,InterruptedException{
finalCompletableFuturefutureOne=CompletableFuture.supplyAsync(()->{
try{
Thread.sleep(3000);
}catch(InterruptedExceptione){
System.out.println("futureOneInterruptedException");
}
return"futureOneResult";
});
finalCompletableFuturefutureTwo=CompletableFuture.supplyAsync(()->{
try{
Thread.sleep(6000);
}catch(InterruptedExceptione){
System.out.println("futureTwoInterruptedException");
}
return"futureTwoResult";
});
CompletableFuturefuture=CompletableFuture.allOf(futureOne,futureTwo);
System.out.println(future.get());
//CompletableFuturecompletableFuture=CompletableFuture.anyOf(futureOne,futureTwo);
//System.out.println(completableFuture.get());
}

3911a466-0c0f-11ed-ba43-dac502259ad0.png3921c3e6-0c0f-11ed-ba43-dac502259ad0.png(3)completedFuture这个方法我没懂他是干啥的,源码就是返回一个值。感觉没啥意义。

392f1a3c-0c0f-11ed-ba43-dac502259ad0.png(4)取值方法,除了get还有一个getNow(); 这个就比较特殊了。

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

393ba2d4-0c0f-11ed-ba43-dac502259ad0.png(5)join方法跟线程的join用法差不多。

394cbff6-0c0f-11ed-ba43-dac502259ad0.png(6)whenXXX,在一个任务执行完成之后调用的方法。

这个有三个名差不多的方法:whenComplete、whenCompleteAsync、还有一个是whenCompleteAsync用自定义Executor

395996a4-0c0f-11ed-ba43-dac502259ad0.png首先看一下这个whenComplete实例方法。这个就是任务执行完毕调用的,传入一个action,这个方法的执行线程是当前线程,意味着会阻塞当前线程。

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

396f87e8-0c0f-11ed-ba43-dac502259ad0.png下面是任务执行的线程的探索。

397f5538-0c0f-11ed-ba43-dac502259ad0.png398f4f7e-0c0f-11ed-ba43-dac502259ad0.png根据测试得出的结论是:如果调用whenComplete的中途,还发生了其他事情,图中的主线程的sleep(400);导致completableFuture这个任务执行完毕了,那么就使用主线程调用。

如果调用的中途没有发生其他任务且在触碰到whenComplete方法时completableFuture这个任务还没有彻底执行完毕那么就会用completableFuture这个任务所使用的线程。

下面是whenCompleteAsync方法。这个方法就是新创建一个异步线程执行。所以不会阻塞。

39a35bae-0c0f-11ed-ba43-dac502259ad0.png(7) then方法瞅着挺多的,实际上就是异不异步和加不加自定义Executor

39b0ea4e-0c0f-11ed-ba43-dac502259ad0.png

注:whenComplete中出现的问题在then中测试不存在、使用的就是上一个任务的线程。这个thenCompose就是一个任务执行完之后可以用它的返回结果接着执行的方法,方法返回的是另一个你期盼泛型的结果。

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

39c652da-0c0f-11ed-ba43-dac502259ad0.png下面介绍一下thenCombine

这个combine的理解就是结合两个任务的结果。

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

thenRun就是这个任务运行完,再运行下一个任务,感觉像是join了一下。

39eb6138-0c0f-11ed-ba43-dac502259ad0.png其余不再介绍,大同小异。

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

像thenAccept(Consumer);这样的就是有入参,但是没有返回值的。详情在上文中有过关于函数式接口的叙述。

审核编辑:彭静

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 接口
    +关注

    关注

    33

    文章

    8441

    浏览量

    150704
  • 函数
    +关注

    关注

    3

    文章

    4276

    浏览量

    62314
  • JDK
    JDK
    +关注

    关注

    0

    文章

    80

    浏览量

    16567

原文标题:Java 8 的异步利器:CompletableFuture源码级解析(建议精读)

文章出处:【微信号:AndroidPush,微信公众号:Android编程精选】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    JDK动态代理的原理

    在Java中,动态代理是一种机制,允许在运行时动态地创建代理对象来代替某个实际对象,从而在其前后执行额外的逻辑。 为什么JDK动态代理只能代理接口实现,原因是JDK动态代理是基于
    的头像 发表于 09-30 10:51 533次阅读

    JDK的安装、环境配置及使用

    ,变量值中输入库名C:\jdk1.5\lib\dt.jar;C:\jdk1.5\lib\tools.jar;然后选择确定。再次选择“系统变量”的“新建”,在变量名中输入path,变量值中输入C
    发表于 12-06 00:19

    2017威哥Java教程视频全集——从入门到精通(基础课程+项目实战)

    ` 本帖最后由 lzr858585 于 2017-9-6 17:04 编辑 课程介绍:本套 Java课程基于最新的 JDK1.8版本,加入更多新特性讲解,尽力打造超全面、极细致、更深入的特点,从
    发表于 09-06 17:02

    Java 那些最常用的工具

    :https://blog.csdn.net/zzti_erlie/article/details/100849192两者的api很相似,如果公司的jdk版本在1.8以上推荐使用jdk1.8新推出的日期
    发表于 06-15 17:18

    JDK 15安装步骤及新特性

    封闭(预览特性)  可以是封闭和或者封闭接口,用来增强 Java 编程语言,防止其他接口扩展或实现它们。  有了这个特性,意味着以
    发表于 12-23 17:36

    java springboot电影购票选座微信小程序源码功能简介

    功能简介后台:会员管理,电影管理,订单管理,系统管理小程序端:首页电影 选座 影院 我的 订单环境准备jdk1.8 mysql5.7 eclipse(idea) navicat后台框架springboot mybatis vue.js bootstrap具体实践...
    发表于 12-30 06:15

    搭建基于HAL库平台的方法分享

    的安装基于HAL库进行STM32开发,由于STM32CubeMX软件是基于Java环境运行的,因此需要安装JRE,可以再JAVA官网上下载安装文件。大家根据自己的操作系统安装合适的版本!(本人一直使用的是JDK1.8)如果平时有使用Java编程的,可以直接下载JDK,它会
    发表于 02-28 12:45

    java jdk6.0官方下载

    java jdk6.0下载如何件: java jdk6.0安装步骤: 第一步 JDK1.6的安装步骤 第一步双击安装文件jdk-6u7-windows-i586-p.exe
    发表于 10-17 11:47 155次下载
    java <b class='flag-5'>jdk</b>6.0官方下载

    虚拟机:CentOS 7通过yum安装JDK1.8的方法

    虚拟机:CentOS 7通过yum安装JDK1.8的方法
    的头像 发表于 07-02 18:02 3150次阅读

    Java开发工具包JDK1.8D安装说明书

    本文档的主要内容介绍的是Java开发工具包JDK1.8D安装说明书资料免费下载。
    发表于 07-16 08:00 33次下载
    Java开发工具包<b class='flag-5'>JDK1.8</b>D安装说明书

    HashMap夺命14问,你能坚持到第几问?

    JDK1.8中,有“数组+链表+红黑树”组成。当链表过长,则会严重影响HashMap的性能,红黑树搜索时间复杂度是O(logn),而链表是O(n)。因此,JDK1.8对数据结构做了进一步的优化,引入了红黑树,链表和红黑树在达到一定条件会进行转换:
    的头像 发表于 04-13 14:40 795次阅读

    基于JDK 1.8来分析Thread的源码

    由上图我们可以看出,Thread实现了Runnable接口,而Runnable在JDK 1.8中被@FunctionalInterface注解标记为函数式
    的头像 发表于 02-06 17:12 580次阅读

    JDK中java.util.HashSet 的介绍

    JDK1.8 中,HashMap 是由 数组+链表+红黑树构成,相对于早期版本的 JDK HashMap 实现,新增了红黑树作为底层数据结构,在数据量较大且哈希碰撞较多时,能够极大的增加检索
    的头像 发表于 10-09 10:50 528次阅读
    <b class='flag-5'>JDK</b>中java.util.HashSet <b class='flag-5'>类</b>的介绍

    JDK中常见的Lamada表达式

    JDK中有许多函数式接口,也会有许多方法会使用函数式接口作为参数,同时在各种源码中也大量使用了这些方法,那么我们在实际工作中应该如何使用!我们就来盘一盘,这样也有助于写出优雅的代码,使我们在阅读源码
    的头像 发表于 10-10 15:07 478次阅读
    <b class='flag-5'>JDK</b>中常见的Lamada表达式

    JDK中java.lang.Arrays 的源码解析

    日常开发中,我们会使用各种工具,利用封装好的轮子,能让我们的开发事半功倍。但是在JDK中,有一个特别的工具——java.lang.Arrays.class,其源码实现还是挺精湛,接下来让我们来
    的头像 发表于 10-11 15:31 559次阅读
    <b class='flag-5'>JDK</b>中java.lang.Arrays <b class='flag-5'>类</b>的源码解析