JUC并发编程之CompletableFuture详解

news2025/1/11 14:49:40

目录

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为null
            if(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.coloradmin.cn/o/787165.html

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

相关文章

(三)InfluxDB入门(借助Web UI)

以下内容来自 尚硅谷&#xff0c;写这一系列的文章&#xff0c;主要是为了方便后续自己的查看&#xff0c;不用带着个PDF找来找去的&#xff0c;太麻烦&#xff01; 第 3 章 InfluxDB入门&#xff08;借助Web UI&#xff09; 借助Web UI&#xff0c;我们可以更好地理解InfluxD…

SiddonGpu编译过程记录

1. 还是想要能够快速生成DRR&#xff0c;用了这个up的代码GitHub - fabio86d/CUDA_DigitallyReconstructedRadiographs: GPU accelerated python library for generation of Digitally Reconstructed Radiographs (March 2018) 在看步骤的时候不是很清晰。尤其是procedure to…

生产环境Session解决方案、Session服务器之Redis

目录 一、服务器配置 二、安装nginx 三、安装配置Tomcat&#xff1a; 四、配置session Session服务器之Redis Redis与Memcached的区别 安装部署redis 一、服务器配置 IP地址 主机名 软件包列表 192.168.100.131 huyang1 nginx 192.168.100.133 huyang3 JDK Tomca…

隧道安全监测解决方案

隧道安全监测 解决方案 一、监测目的 通过监控量测&#xff0c;实现信息化施工&#xff0c;不仅能及时掌握隧道实际的地质情况&#xff0c;掌握隧道围岩、支护衬砌结构的受力特征和变形情况&#xff0c;据此可以尽早发现塌方、大变形等灾害征兆&#xff0c;及时采取措施&…

Qgis二次开发-加载高德在线地图失败报错Raster layer Cannot instantiate the ‘wms‘ data provider

1.加载在线的高德地图 以下是在线高德地图地址。 http://webrd01.is.autonavi.com/appmaptile?x{x}&y{y}&z{z}&langzh_cn&size1&scale1&style8 以下是代码示例&#xff1a; #include <QApplication> #include <QMainWindow> #include …

Django学习笔记-表单(forms)的使用

在Django中提供了了form表单&#xff0c;可以更为简单的创建表单模板信息&#xff0c;简化html的表单。 一、网页应用程序中表单的应用 表单通常用来作为提交数据时候使用。 1.1 创建表单模板文件夹 在项目文件夹下创建一个template文件夹&#xff0c;用于存储所有的html模…

Integer 和 int

一、区别 1.Integer是int的包装类&#xff0c;int则是java的一种基本的数据类型&#xff1b; 2.Integer变量必须实例化之后才能使用&#xff0c;而int变量不需要实例化&#xff1b; 3.Integer实际是对象的引用&#xff0c;当new一个Integer时&#xff0c;实际上生成一个指针指向…

Linux vsftp三种模式的简单配置部署

环境&#xff1a;Debian 6.1.27-1kali1 (2023-05-12) vsftpd 安装 --查看是否当前系统是否已安装 apt list --installed | grep vsftpd 没有安装的话&#xff0c;就正常安装 apt-get update apt-get install vsftpd 一、匿名用户模式 分享一些不重要文件&#xff0c;任…

AI学习笔记二:YOLOV5环境搭建及测试全过程

若该文为原创文章&#xff0c;转载请注明原文出处。 记录yolov5从环境搭建到测试全过程。 一、运行环境 1、系统&#xff1a;windows10 &#xff08;无cpu) 2、yolov5版本&#xff1a;yolov5-5.0 3、python版本&#xff1a;py3.8 在创建虚拟环境前需要先把miniconda3和py…

Font Awesome 5使用

Font Awesome 5介绍 Font Awesome 5 的专业版有7842个图标。 要使用免费的Font Awesome 5 图标&#xff0c;您可以选择下载Font Awesome库&#xff0c;或者在Font Awesome注册一个帐户&#xff0c;并获得一个代码&#xff08;称为KIT CODE&#xff09;&#xff0c;以便在将Fo…

Wireshark抓包分析教程(ubuntu版本)

安装 first&#xff0c;多亏我们的C知道&#xff0c;成功安装了wireshark&#xff0c; Steps are as following&#xff1a; 添加wireshark的软件源(PPA:personal package archive(档案)) sudo apt-add-repository ppa:wireshark-dev/stable ppa:wireshark-dev/stable 是一个…

Quad Remesher 参数效果记录

基本属性 Detect Hard-Edges by angle 这个属性最好开启&#xff0c;它用于保持必要的边缘

WEB:ics-06

背景知识 burpsuite的使用 题目 所有都尝试点了一遍&#xff0c;只有报表中心可以打开 先查看源代码&#xff0c;没有发现有用的信息 选择了日期范围但是毫无反应&#xff0c;这里发现url中有一个id1&#xff0c;尝试抓包爆破 使用bp进行抓包 设置有效载荷配置&#xff0c;开…

(链表) 剑指 Offer 24. 反转链表 ——【Leetcode每日一题】

❓剑指 Offer 24. 反转链表 难度&#xff1a;简单 定义一个函数&#xff0c;输入一个链表的头节点&#xff0c;反转该链表并输出反转后链表的头节点。 示例: 输入: 1->2->3->4->5->NULL 输出: 5->4->3->2->1->NULL 限制&#xff1a; 0 < …

小程序如何删除/上架/下架商品

在小程序中&#xff0c;产品的删除、上架和下架是常见的操作&#xff0c;可以根据实际需求来管理商品的展示与销售。下面将介绍如何在小程序中删除上架下架商品的具体步骤。 进入商品管理页面&#xff0c; 在个人中心点击管理入口&#xff0c;然后找到“商品管理”菜单并点击。…

Git----分布式版本控制系统

1. 简介 Git是一个开源的分布式版本控制系统&#xff0c;用于敏捷高效地处理任何或大或小的项目。它是世界上目前最先进的分布式版本控制系统。 Git是Linus Torvalds为了帮助管理Linux内核开发而开发的一个开放源代码的版本控制软件。 Git与常用的版本控制工具CVS、Subversio…

2023最新版本Activiti7系列-流程中的任务

流程中的任务 1.用户任务 用户任务:用于定义流程中需要人工参与的任务。 用户任务可以在流程中创建并分配给特定的用户或用户组。当流程执行到用户任务时&#xff0c;流程将暂停&#xff0c;并等待相应的用户完成该任务。完成用户任务后&#xff0c;流程将继续执行。 用户任…

系统架构设计师视频教程笔记

根据历年真题倒翻官方教程&#xff0c;发现好多内容没有&#xff0c;据说2022新版改动很大 既然是真题&#xff0c;肯定有参考价值&#xff0c;那就看看旧视频吧 考试分享 软考高级架构师&#xff0c;有用但不大【通过后分享】 up主公司要接政府项目&#xff0c;有考证需求 …

Spring:Annontation

Bean 配置注解Autowired 注解context:component-scan 标签 配置 在 Spring 中&#xff0c;配置 bean 实例一般使用 xml 配置方式或注解&#xff08;Annontation&#xff09; 方式进行配置。 注解 注解&#xff08;Annontation&#xff09;&#xff0c;是在原有代码和逻辑下通…

数据库字段变更监控平台设计开发

序&#xff1a; 在开发过程中&#xff0c;在值班解决客服问题时&#xff0c;在分析定位别人写的业务代码问题时&#xff0c;重点是不是自己写的代码&#xff0c;只看到了数据库中落库最终数据&#xff0c;并不知道业务逻辑问题发生时数据库表中当时数据情况&#xff1f;如果能知…