JUC并发编程之CompletableFuture详解

目录

1.Future接口

1.1 Future介绍

1.1.1 FutureTask

1.1.2 代码示例

2. CompletableFuture

2.1 基本概念

 2.2 代码示例

2.2.1 创建CompletableFuture

2.2.2 函数式接口(补充)

 2.2.3 异步任务组合


1.Future接口

1.1 Future介绍

JUC并发编程中的Future接口是Java 5中引入的一种异步编程机制,用于表示一个可能在未来完成的计算结果。它允许我们提交一个任务给线程池或其他执行器执行,并且可以通过Future对象获取任务执行的结果或者判断任务是否已经完成。

Future接口的基本方法如下:

  1. isDone(): 判断任务是否已经完成。
  2. get(): 获取任务的计算结果,如果任务还未完成,调用该方法将会阻塞,直到任务完成并返回结果。
  3. get(long timeout, TimeUnit unit): 获取任务的计算结果,但最多等待指定时间,如果任务在指定时间内没有完成,则会抛出超时异常。
  4. cancel(boolean mayInterruptIfRunning): 尝试取消任务的执行,如果任务已经开始执行,参数mayInterruptIfRunning决定是否中断任务。
  5. isCancelled(): 判断任务是否已经被取消。

1.1.1 FutureTask

FutureTask是Java并发编程中的一个类,它实现了Future接口,并且是Runnable的实现类。FutureTask可以用来包装一个Callable或Runnable任务,使得我们可以在另一个线程中执行该任务,并且在未来的某个时间点获取任务的结果。FutureTask提供了一些方法来获取任务的执行结果,以及判断任务是否已经完成或取消。

如下图所示:

 

 FutureTask实现了Future接口和Runnable接口,因此可以调用FutureRunnable的方法。

1.1.2 代码示例

import java.util.concurrent.*;public class FutureExample {public static void main(String[] args) {// 创建一个线程池ExecutorService executor = Executors.newFixedThreadPool(1);// 提交一个异步任务Future<String> future = executor.submit(() -> {try {Thread.sleep(2000); // 模拟耗时任务} catch (InterruptedException e) {e.printStackTrace();}return "异步任务执行完成";});System.out.println("异步任务已经提交");// 等待任务完成,并获取结果try {String result = future.get(); // 这里会阻塞,直到任务完成并返回结果System.out.println(result);} catch (InterruptedException | ExecutionException e) {e.printStackTrace();}// 关闭线程池executor.shutdown();}
}

在上面的示例中,我们使用ExecutorService创建一个拥有一个线程的线程池,然后通过submit()方法提交一个简单的异步任务,该任务在执行过程中睡眠2秒钟,然后返回一个字符串。接着,我们使用Futureget()方法获取任务的执行结果,这里get()方法将会阻塞直到任务完成并返回结果。

需要注意的是,Futureget()方法在获取结果时会阻塞,这可能会导致程序在等待过程中无法执行其他任务。为了更好地处理异步任务,Java 8引入了CompletableFuture,它提供了更多灵活的方法来处理异步任务和任务组合,可以更好地解决Future在并发编程中的一些限制。

2. CompletableFuture

2.1 基本概念

CompletableFuture是Java并发工具包(Java Util Concurrent,JUC)中的一部分,自Java 8版本引入,用于简化异步编程和并发任务的处理。它是java.util.concurrent包中的一个类,提供了强大的功能来处理异步操作和任务之间的依赖关系。

CompletableFuture是一个可完成或可编程的Future。它可以被手动完成,也可以用于串联多个异步操作,并在操作完成时触发后续的操作。它提供了一系列方法来组合和转换异步任务,例如thenApply(), thenAccept(), thenCombine(), thenCompose(), thenRun()等,CompletableFuture提供了一种类似观察者模式的机制,可以让任务执行完成后通知监听的一方。

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


如下图:

 2.2 代码示例

2.2.1 创建CompletableFuture

CompletableFuture.runAsync(): 创建一个没有返回值的CompletableFuture,用于执行异步任务。

 ExecutorService threadPool = Executors.newFixedThreadPool(1);CompletableFuture<Void> completableFuture = CompletableFuture.runAsync(() -> {System.out.println(Thread.currentThread().getName());},threadPool);

CompletableFuture.supplyAsync(): 创建一个具有返回值的CompletableFuture,用于执行异步计算。

 ExecutorService threadPool = Executors.newFixedThreadPool(1);CompletableFuture<String> stringCompletableFuture = CompletableFuture.supplyAsync(() -> {return "hello";}, threadPool);System.out.println(stringCompletableFuture.get());threadPool.shutdown();

CompletableFutureFuture的功能加强版,减少阻塞和轮询,可以传入回调对象,当异步任务完成后或发生异常时,自动调用回调对象的回调方法。代码如下:

  CompletableFuture<String> stringCompletableFuture = CompletableFuture.supplyAsync(() -> {return "hello";}).whenComplete((v,e)->{ //v表示返回的结果,e为异常,没有异常的时候e为nullif(e==null){System.out.println("异步线程完成任务返回结果为 "+v);}}).exceptionally(e->{e.printStackTrace();System.out.println("goods");return null;});

在这里我们未使用我们自定义的线程池,这里会自动使用默认的线程池。

注意:当我们在异步线程的逻辑处理中加上Thread.sleep(1000)代码后再次运行会发现如下面的结果:

 因为主线程是用户线程处理速度太快,CompletableFuture默认使用的线程池会立刻关闭。所以推荐使用自定义线程池。

2.2.2 函数式接口(补充)

在这里用到不少函数式接口,故在这里进行简单介绍一下。

Java函数式接口是Java 8版本中引入的一个特性,它是一种只包含一个抽象方法的接口。函数式接口可以被认为是用来支持Lambda表达式的接口,Lambda表达式可以被转换为函数式接口的实例。

Java函数式接口有以下特点:

  1. 只包含一个抽象方法:函数式接口只能有一个未实现的抽象方法。可以有默认方法和静态方法,但只有一个抽象方法。

  2. @FunctionalInterface注解:为了确保一个接口是函数式接口,可以使用@FunctionalInterface注解进行标记。如果接口不符合函数式接口的定义,编译器会给出错误提示。

如下图:

 2.2.3 异步任务组合

thenApply(): 对前一个阶段的计算结果进行转换

CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> "Hello").thenApply(s -> s + " World");

thenAccept(): 处理前一个阶段的计算结果,但不返回结果。

CompletableFuture.supplyAsync(() -> "Hello").thenAccept(s -> System.out.println(s + " World"));

thenRun(): 在上一个阶段的任务完成后执行指定的动作,没有返回值。

   CompletableFuture<Void> hello = CompletableFuture.supplyAsync(() -> "Hello").thenRun(() -> {System.out.println("hello");});

thenCombine(): 组合两个独立的CompletableFuture的结果。

CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> "Hello");
CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> " World");
CompletableFuture<String> combinedFuture = future1.thenCombine(future2, (s1, s2) -> s1 + s2);

handle():  因为thenApply方法是需要存在依赖关系,所以当前步骤错了,就不会继续往下一步执行,而handle可以

   CompletableFuture.supplyAsync(()->{System.out.println("执行第一步");Integer i=1/0;return 1;}).handle((v,e)->{System.out.println("执行第二步");return v+2;}).handle((v,e)->{System.out.println("执行第三步");return v+3;}).whenComplete((v,e)->{if (e == null) {System.out.println("最后结果:"+v);}}).exceptionally(e->{e.printStackTrace();return null;});}

applyToEither(): CompletableFuture类中的一个方法,用于处理两个CompletableFuture的结果,只要其中任意一个完成就可以执行指定的转换函数。applyToEither方法在异步编程中常用于多个任务竞争的场景,我们希望只要其中一个任务先完成就可以对其结果进行处理,而不需要等待其他任务的完成。

CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> {try {TimeUnit.SECONDS.sleep(3);} catch (InterruptedException e) {e.printStackTrace();}return "Future 1";});CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> {try {TimeUnit.SECONDS.sleep(2);} catch (InterruptedException e) {e.printStackTrace();}return "Future 2";});CompletableFuture<String> resultFuture = future1.applyToEither(future2, s -> s + " wins!");resultFuture.thenAccept(System.out::println); // 输出较快的任务的结果

总结(摘录网图) 

在这里插入图片描述

注意:

1. 没有传入自定义线程池,都用默认线程池ForkJoinPool;
2. 如果你执行第一个任务的时候,传入了一个自定义线程池:
调用thenRun方法执行第二个任务时,则第二个任务和第一个任务是共用同一个线程池。
调用thenRunAsync执行第二个任务时,则第一个任务使用的是你自己传入的线程池,第二个任务使用的是ForkJoinPool线程池l
备注
有可能处理太快,系统优化切换原则,直接使用main线程处理

其它如: thenAccept和thenAcceptAsync,thenApply和thenApplyAsync等,它们之间的区别也是同理。

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

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

相关文章

二叉树题目:从根到叶的二进制数之和

文章目录 题目标题和出处难度题目描述要求示例数据范围 解法一思路和算法代码复杂度分析 解法二思路和算法代码复杂度分析 题目 标题和出处 标题&#xff1a;从根到叶的二进制数之和 出处&#xff1a;1022. 从根到叶的二进制数之和 难度 3 级 题目描述 要求 给你二叉树…

Git标签管理(对版本打标签,起别名)

tag 理解标签创建标签git tag [name]git show [tagname] 操作标签删除标签git tag -d < tagname > 推送某个标签到远程git push origin < tagname > 理解标签 标签 tag &#xff0c;可以简单的理解为是对某次 commit 的⼀个标识&#xff0c;相当于起了⼀个别名。 …

C# winform子窗口向父窗口传值

这里我使用一个简单的方法。只需要在父窗口定义一个静态变量就行。 父窗体为Form1,子窗体为Form2。 public static int get_num0; 子窗体直接给get_num赋值即可。 Form1.get_num2; 这样父窗体就能获得get_num修改后这个值了

游戏引擎UE如何革新影视行业?创意云全面支持UE云渲染

虚幻引擎UE&#xff08;Unreal Engine&#xff09;作为一款“殿堂级”的游戏引擎&#xff0c;占据了全球80%的商用游戏引擎市场&#xff0c;但如果仅仅将其当做游戏开发的工具&#xff0c;显然是低估了它的能力。比如迪士尼出品的电视剧《曼达洛人》、电影《狮子王》等等都使用…

Matlab 点云曲面特征提取

文章目录 一、简介二、实现代码2.1基于k个邻近点2.2基于邻近半径参考资料一、简介 这里基于每个点的邻域协方差来获取点云中具有的曲面几何特征的点,计算方式如下图所示: 二、实现代码 2.1基于k个邻近点 SurfaceVar.m %% *******</

【网络代理】(三)Docker+Haproxy 搭建四层代理

目录 1.1 创建 web 服务器镜像 1.2 启动 web 服务器容器 2.1 编写 haproxy 配置文件 2.2 拉取 haproxy 镜像 2.3 启动 haproxy 容器 3.1 访问 8000 端口 3.2 查看 web 服务器容器日志 附录&#xff1a;haproxy 仪表板 1.1 创建 web 服务器镜像 编写一个 Docke…

uniapp实战

上面是tab栏&#xff0c;下面是swiper&#xff0c;&#xff0c;tab和swiper和 红色滑块 动态变化&#xff0c;&#xff0c; 遇到的问题&#xff1a; 往下滚动 tab栏 吸顶&#xff1a; position:sticky; z-index:99; top:0;swiper切换触发 change 事件&#xff0c; :current …

Golang GORM 模型定义

模型定义 参考文档&#xff1a;https://gorm.io/zh_CN/docs/models.html 模型一般都是普通的 Golang 的结构体&#xff0c;Go的基本数据类型&#xff0c;或者指针。 模型是标准的struct,由Go的基本数据类型、实现了Scanner和Valuer接口的自定义类型及其指针或别名组成&#x…

Android TelephonyManager双卡获取数据开启状态异常的可能原因

背景 应用内不指定subId获取数据状态可能会错误&#xff0c;因为可能拿到voice的能力&#xff0c;而非data。 代码逻辑 1、通过TelephonyManager的isDataEnabled()没有指定subId时&#xff0c;调用内部方法isDataEnabledForReason&#xff0c;传入getId()参数以指定subid&am…

FUNBOX_SCRIPTKIDDIE靶机详解

FUNBOX_SCRIPTKIDDIE靶机复盘 这个靶场给了太多的干扰因素&#xff0c;当你打完后反过来再看是非常简单的一个靶场&#xff0c;但是你打的过程中却会觉得非常难&#xff0c;干扰因素实在天多了。 题目中给了说加一条hosts&#xff0c;实际没用上。 对IP进行一个单独扫描后发现…

机器学习深度学习——torch.nn模块

机器学习&&深度学习——torch.nn模块 卷积层池化层激活函数循环层全连接层 torch.nn模块包含着torch已经准备好的层&#xff0c;方便使用者调用构建网络。 卷积层 卷积就是输入和卷积核之间的内积运算&#xff0c;如下图&#xff1a; 容易发现&#xff0c;卷积神经网…

uniapp 微信小程序 placeholder字体、颜色自定义

效果图&#xff1a; 1、template <input type"text" placeholder"搜索标题" placeholder-class"placeholder-style"></input>2、style .placeholder-style{color: #2D94FF; }

微服务探索之路06篇k8s配置文件Yaml部署Redis使用Helm部署MongoDB和kafka

1 安装Redis 1.1创建配置文件redis.conf 切换到自己的目录下如本文是放在/home/ubuntu下 cd /home/ubuntuvim redis.conf bind 0.0.0.0 protected-mode yes port 6379 requirepass qwe123456 tcp-backlog 511 timeout 0 tcp-keepalive 300 daemonize no pidfile /var/run/r…

生产者消费者模型

生产者消费者模型 文章目录 生产者消费者模型概念原则优点 基于BlockingQueue的生产者消费者模型BlockingQueue模拟实现单生产者消费者模型基于计算任务和存储任务的生产者消费者模型 概念 生产者消费者模式就是通过一个容器来解决生产者和消费者的强耦合问题生产者和消费者彼…

代码随想录| 图论02●695岛屿最大面积 ●1020飞地的数量 ●130被围绕的区域 ●417太平洋大西洋水流问题

#695岛屿最大面积 模板题&#xff0c;很快.以下两种dfs&#xff0c;区别是看第一个点放不放到dfs函数中处理&#xff0c;那么初始化的area一个是1一个是0 int dir[4][2]{0,1,0,-1,1,0,-1,0};void dfs(int x, int y,int n, int m, int &area,vector<vector<bool>…

2023最新谷粒商城笔记之Sentinel概述篇(全文总共13万字,超详细)

Sentinel概述 服务流控、熔断和降级 什么是熔断 当扇出链路的某个微服务不可用或者响应时间太长时&#xff0c;会进行服务的降级&#xff0c;**进而熔断该节点微服务的调用&#xff0c;快速返回错误的响应信息。**检测到该节点微服务调用响应正常后恢复调用链路。A服务调用B服…

构建高效供应商管理体系,提升企业采购能力

随着企业采购规模的不断扩大和全球化竞争的加剧&#xff0c;供应商管理变得越来越重要。构建一个高效的供应商管理体系是企业提升采购能力、降低采购成本的关键一环。本文将重点探讨供应商管理体系的意义和作用&#xff0c;并介绍如何构建一个高效的供应商管理体系。 一、供应商…

SpringBoot复习:(1)常用的SpringApplication.run返回的容器的具体类型是哪个?

run方法中调用了createApplicationContext方法 createApplicationContext方法代码如下&#xff1a; 其中create代码如下&#xff1a; 可见返回的是AnnotationConfigServletWebServerApplicationContext()

【搜索引擎Solr】配置 Solr 以获得最佳性能

Apache Solr 是广泛使用的搜索引擎。有几个著名的平台使用 Solr&#xff1b;Netflix 和 Instagram 是其中的一些名称。我们在 tajawal 的应用程序中一直使用 Solr 和 ElasticSearch。在这篇文章中&#xff0c;我将为您提供一些关于如何编写优化的 Schema 文件的技巧。我们不会讨…

基于Python+WaveNet+CTC+Tensorflow智能语音识别与方言分类—深度学习算法应用(含全部工程源码)

目录 前言总体设计系统整体结构图系统流程图 运行环境Python 环境Tensorflow 环境 模块实现1. 方言分类数据下载及预处理模型构建模型训练及保存 2. 语音识别数据预处理模型构建模型训练及保存 3. 模型测试功能选择界面语言识别功能实现界面方言分类功能实现界面 系统测试1. 训…