CompletableFuture编排异步线程

news2024/11/6 7:12:07

CompletableFuture 是 Java 8 引入的一种新的 Future,设计目的是为了编写非阻塞的异步代码。

传统异步编程方式

传统异步编程方式获得异步任务值,首先我们得通过future task ,然后创建一个实现callable内部类,或者通过lambda的表达式,然后再结合thread,或者线程池的方式去执行它,具体代码如下。

import java.util.concurrent.*;

public class CompletableFutureTest {

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        FutureTask<String> futureTask = new FutureTask<>(new Callable<String>() {
            @Override
            public String call() throws Exception {
                Thread.sleep(2000);
                return "futureTask 执行完成";
            }
        });

        new Thread(futureTask).start();
        //get()方法作用:以阻塞的方式获取任务执行结果
        System.out.println("new Thread的方式获取结果:" + futureTask.get());

        ExecutorService executorService = Executors.newSingleThreadExecutor();
        executorService.submit(futureTask);
        //get()方法作用:以阻塞的方式获取任务执行结果
        System.out.println("线程池的方式获取结果:" + futureTask.get());
        executorService.shutdown(); // 关闭线程池

        System.out.println("TODO...");
    }

}

运行结果:
在这里插入图片描述
可以看出整个实现过程比较麻烦,想要获得返回值会调用它的get()方法,会阻塞后面的代码,如果后面的代码并不依赖future task的返回值的话,其实我们更希望以并行的方式去执行,性能肯定是更高的,那么我们结合CompletableFuture来进行改造。

CompletableFuture实现异步编程方式

1.异步执行

supplyAsync

supplyAsync是创建带有返回值的异步任务。它有如下两个方法,一个是使用默认线程池(ForkJoinPool.commonPool())的方法,一个是带有自定义线程池的重载方法。

// 带返回值异步请求,默认线程池
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier)
 
// 带返回值的异步请求,可以自定义线程池
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier, Executor executor)

具体代码:

import java.util.concurrent.*;

public class CompletableFutureTest {

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        CompletableFuture<String> completableFuture = CompletableFuture.supplyAsync(() -> {
            return "默认线程池执行有返回值的任务";
        });
        System.out.println(completableFuture.get());//get()方法抛出ExecutionException, InterruptedException检查时异常,程序必须做处理

        // 自定义线程池
        ExecutorService executorService = Executors.newSingleThreadExecutor();
        CompletableFuture<String> completableFutureWithThreadExecutor = CompletableFuture.supplyAsync(() -> {
            return "自定义线程池执行有返回值的任务";
        },executorService);
        System.out.println(completableFutureWithThreadExecutor.join());//join()方法只抛出运行时异常,程序可不做处理
        
    }

}

运行结果:

在这里插入图片描述

runAsync

runAsync是创建没有返回值的异步任务。它有如下两个方法,一个是使用默认线程池(ForkJoinPool.commonPool())的方法,一个是带有自定义线程池的重载方法,具体代码如下。

import java.util.concurrent.*;

public class CompletableFutureTest {

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        CompletableFuture<Void> completableFuture = CompletableFuture.runAsync(() -> {
            System.out.println("默认线程池执行没有返回值的任务");
        });
        System.out.println("result:" + completableFuture.get());

        // 自定义线程池
        ExecutorService executorService = Executors.newSingleThreadExecutor();
        CompletableFuture<Void> completableFutureWithThreadExecutor = CompletableFuture.runAsync(() -> {
            System.out.println("自定义线程池执行没有返回值的任务");

        },executorService);
        System.out.println("result:" + completableFutureWithThreadExecutor.get());

    }

}

运行结果:
在这里插入图片描述

2.获取任务结果的方法

// 如果完成则返回结果,否则就抛出具体的异常
public T get() throws InterruptedException, ExecutionException 
 
// 最大时间等待返回结果,否则就抛出具体异常
public T get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException
 
// 完成时返回结果值,否则抛出unchecked异常。为了更好地符合通用函数形式的使用,如果完成此 CompletableFuture所涉及的计算引发异常,则此方法将引发unchecked异常并将底层异常作为其原因
public T join()
 
// 如果完成则返回结果值(或抛出任何遇到的异常),否则返回给定的 valueIfAbsent。
public T getNow(T valueIfAbsent)
 
// 如果任务没有完成,返回的值设置为给定值
public boolean complete(T value)
 
// 如果任务没有完成,就抛出给定异常
public boolean completeExceptionally(Throwable ex) 
 

3.多任务组合处理

allOf / anyOf

allOf:CompletableFuture是多个任务都执行完成后才会执行,只有有一个任务执行异常,则返回的CompletableFuture执行get方法时会抛出异常,如果都是正常执行,则get返回null,具体代码如下。

import java.util.concurrent.*;

public class CompletableFutureTest {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        CompletableFuture<String> cf1 = CompletableFuture.supplyAsync(() -> {
            try {
                System.out.println(Thread.currentThread() + " cf1 do something....");
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("cf1 任务完成");
            return "cf1 任务完成";
        });

        CompletableFuture<String> cf2 = CompletableFuture.supplyAsync(() -> {
            try {
                System.out.println(Thread.currentThread() + " cf2 do something....");
                int a = 1/0;
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("cf2 任务完成");
            return "cf2 任务完成";
        });
        CompletableFuture<Void> cfAll = CompletableFuture.allOf(cf1, cf2);
        System.out.println("cfAll结果->" + cfAll.get());
    }

}

运行结果:
在这里插入图片描述

anyOf :CompletableFuture是多个任务只要有一个任务执行完成,则返回的CompletableFuture执行get方法时会抛出异常,如果都是正常执行,则get返回执行完成任务的结果,具体代码如下。

import java.util.concurrent.*;

public class CompletableFutureTest {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        CompletableFuture<String> cf1 = CompletableFuture.supplyAsync(() -> {
            try {
                System.out.println(Thread.currentThread() + " cf1 do something....");
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("cf1 任务完成");
            return "cf1 任务完成";
        });

        CompletableFuture<String> cf2 = CompletableFuture.supplyAsync(() -> {
            try {
                System.out.println(Thread.currentThread() + " cf2 do something....");
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("cf2 任务完成");
            return "cf2 任务完成";
        });
        CompletableFuture<Object> cfAll = CompletableFuture.anyOf(cf1, cf2);
        System.out.println("cfAll结果->" + cfAll.get());
    }

}

运行结果:
在这里插入图片描述

4.异步回调处理

thenRun和thenRunAsync

thenRun表示某个任务执行完成后执行的动作,即回调方法,无入参,无返回值,具体代码如下。

import java.util.concurrent.*;

public class CompletableFutureTest {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        CompletableFuture<Integer> cf1 = CompletableFuture.supplyAsync(() -> {
            System.out.println(Thread.currentThread() + " cf1 do something....");
            return 1;
        });

        CompletableFuture<Void> cf2 = cf1.thenRun(() -> {
            System.out.println(Thread.currentThread() + " cf2 do something....");
        });

        //等待任务1执行完成
        System.out.println("cf1结果->" + cf1.get());
        //等待任务2执行完成
        System.out.println("cf2结果->" + cf2.get());
    }

}

运行结果:
在这里插入图片描述

thenRunAsync具体代码如下。

import java.util.concurrent.*;

public class CompletableFutureTest {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        CompletableFuture<Integer> cf1 = CompletableFuture.supplyAsync(() -> {
            System.out.println(Thread.currentThread() + " cf1 do something....");
            return 1;
        });

        CompletableFuture<Void> cf2 = cf1.thenRunAsync(() -> {
            System.out.println(Thread.currentThread() + " cf2 do something....");
        });

        //等待任务1执行完成
        System.out.println("cf1结果->" + cf1.get());
        //等待任务2执行完成
        System.out.println("cf2结果->" + cf2.get());
    }

}

运行结果:
在这里插入图片描述
从上面代码和测试结果我们发现thenRun和thenRunAsync区别在于,使用thenRun方法时子任务与父任务使用的是同一个线程,而thenRunAsync在子任务中可能是另起一个线程执行任务,并且thenRunAsync可以自定义线程池,默认的使用ForkJoinPool.commonPool()线程池。

thenAccept和thenAcceptAsync

thenAccep表示某个任务执行完成后执行的动作,即回调方法,会将该任务的执行结果即方法返回值作为入参传递到回调方法中,无返回值,具体代码如下。

public class CompletableFutureTest {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        CompletableFuture<Integer> cf1 = CompletableFuture.supplyAsync(() -> {
            System.out.println(Thread.currentThread() + " cf1 do something....");
            return 1;
        });

        CompletableFuture<Void> cf2 = cf1.thenAccept((result) -> {
            System.out.println(Thread.currentThread() + " cf2 do something....,入参:" + result);
        });

        //等待任务1执行完成
        System.out.println("cf1结果->" + cf1.get());
        //等待任务2执行完成
        System.out.println("cf2结果->" + cf2.get());
    }

}

运行结果:
在这里插入图片描述
测试结果我们发现thenAccep和thenAccepAsync区别在于,使用thenAccep方法时子任务与父任务使用的是同一个线程,而thenAccepAsync在子任务中可能是另起一个线程执行任务,并且thenAccepAsync可以自定义线程池,默认的使用ForkJoinPool.commonPool()线程池。

thenApply和thenApplyAsync

thenApply 表示某个任务执行完成后执行的动作,即回调方法,会将该任务的执行结果即方法返回值作为入参传递到回调方法中,带有返回值,具体代码如下。

import java.util.concurrent.*;

public class CompletableFutureTest {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        CompletableFuture<Integer> cf1 = CompletableFuture.supplyAsync(() -> {
            System.out.println(Thread.currentThread() + " cf1 do something....");
            return 1;
        });

        CompletableFuture<Integer> cf2 = cf1.thenApplyAsync((result) -> {
            System.out.println(Thread.currentThread() + " cf2 do something....");
            result += 2;
            return result;
        });
        //等待任务1执行完成
        System.out.println("cf1结果->" + cf1.get());
        //等待任务2执行完成
        System.out.println("cf2结果->" + cf2.get());
    }

}

运行结果:
在这里插入图片描述
从上面代码和测试结果我们发现thenApply和thenApplyAsync区别在于,使用thenApply方法时子任务与父任务使用的是同一个线程,而thenApplyAsync在子任务中是另起一个线程执行任务,并且thenApplyAsync可以自定义线程池,默认的使用ForkJoinPool.commonPool()线程池。

whenComplete和whenCompleteAsync

whenComplete是当某个任务执行完成后执行的回调方法,会将执行结果或者执行期间抛出的异常传递给回调方法,如果是正常执行则异常为null,回调方法对应的CompletableFuture的result和该任务一致,如果该任务正常执行,则get方法返回执行结果,如果是执行异常,则get方法抛出异常,具体代码如下。

import java.util.concurrent.*;

public class CompletableFutureTest {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        CompletableFuture<Integer> cf1 = CompletableFuture.supplyAsync(() -> {
            System.out.println(Thread.currentThread() + " cf1 do something....");
            int a = 1/0;
            return 1;
        });

        CompletableFuture<Integer> cf2 = cf1.whenComplete((result, e) -> {
            System.out.println("上个任务结果:" + result);
            System.out.println("上个任务抛出异常:" + e);
            System.out.println(Thread.currentThread() + " cf2 do something....");
        });

//        //等待任务1执行完成
//        System.out.println("cf1结果->" + cf1.get());
//        //等待任务2执行完成
        System.out.println("cf2结果->" + cf2.get());
    }

}

运行结果:
在这里插入图片描述

handle和handleAsync

跟whenComplete基本一致,区别在于handle的回调方法有返回值,具体代码如下。

import java.util.concurrent.*;

public class CompletableFutureTest {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        CompletableFuture<Integer> cf1 = CompletableFuture.supplyAsync(() -> {
            System.out.println(Thread.currentThread() + " cf1 do something....");
            //int a = 1/0;
            return 1;
        });

        CompletableFuture<Integer> cf2 = cf1.handle((result, e) -> {
            System.out.println(Thread.currentThread() + " cf2 do something....");
            System.out.println("上个任务结果:" + result);
            System.out.println("上个任务抛出异常:" + e);
            return result+2;
        });

        //等待任务2执行完成
        System.out.println("cf2结果->" + cf2.get());
    }

}

运行结果:
在这里插入图片描述

总结

以上例子展示了 CompletableFuture 的基本使用方法,包括创建异步任务、结果处理、异常处理和结果组合。在实际开发中,你可以根据需要组合使用这些方法来实现复杂的异步逻辑。

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

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

相关文章

JAVA学习笔记31(IO流)

1.IO流 1.文件流 ​ *文件在程序中是以流的形式来操作的 2.常用文件操作 1.创建文件对象 1.new File(String pathname) //根据路径构建一个File对象 main() {}public void create01() {String filePath "e:\\news1.txt";File filePath new File(filePath);tr…

人人都会给视频换脸_出色的AI换脸软件离线版你可以把视频换上明星脸

网盘下载 简单几步骤: 1、找个人脸照片&#xff0c;正面高清 2、找个视频&#xff0c;最好是单人的视频&#xff0c;或者只有一个女的&#xff0c;这样可以按照条件换脸 3、点击开始&#xff0c;等待完成即可&#xff08;显卡勾选显卡&#xff0c;显卡不行选择CPU&#xff09;…

最新win11配置cuda以及cudnn补丁教程

1、首先使用指令 nvidia-smi 查看电脑支持的**最高cuda**版本&#xff0c;例如&#xff1a;本机 12.2 2、进入CUDA下载cuda安装包 https://developer.nvidia.com/cuda-toolkit-archive 2、点击上方绿色的链接&#xff0c;按照图中序号选择的即可&#xff0c;最后点击下载。 …

【MySQL 数据宝典】【磁盘结构】- 004 redolog 重做日志

一、背景介绍 持久性要求&#xff1a; 对于已提交的事务&#xff0c;即使系统发生崩溃&#xff0c;其对数据库的更改也不能丢失。问题&#xff1a; 在事务提交前将所有修改的页面刷新到磁盘浪费资源。随机IO导致刷新速度慢。 解决方案&#xff1a; 【数据副本】记录事务执行过…

中仕公考:广东省2024高校毕业生‘三支一扶‘开始报名

广东省2024高校毕业生三支一扶于今日4月22日正式开始报名&#xff0c;报名人员请于2024年4月22日9:00-4月26日17:00登录广东省高校毕业生“三支一扶”计划信息管理系统进行报名。

裤子什么面料适合夏季?必备的五条夏季男生裤子

许多男生朋友应该都发现&#xff0c;想选一条穿着舒服的裤子可真不容易&#xff0c;总是会出现各种情况&#xff0c;列如常见的卡档、显腿粗、显矮等等。甚至还会出现一些质量问题&#xff0c;导致各种闹心。 为了让大家可以找到更适合自己的裤子&#xff0c;我特别花了比较长…

HTML重要标签梳理学习

1、HTML文件的框架 使用VS Code编码时&#xff0c;输入!选中第一个&#xff01;就可以快速生成一个HTML文件框架。 2、标签 <hr> <!--下划线--> <br> <!--换行--> <strong>加粗</strong> &…

ChatGPT4.5:能力大提升,全新体验

说明 ChatGPT4是2023年的5月份发布的&#xff0c;马上就发布一周年了。其他的大语言模型&#xff0c;比如Claude和开源的Lama也相继更新了最新版本。而根据目前国外发布的各种消息来看&#xff0c;ChatGPT4.5也即将发布。 GPT-4.5 Turbo 发布时间 最新消息显示&#xff0c;Op…

Meta 发布 Llama 3:迄今为止最强大的开源大语言模型

Meta 发布了 Llama 3&#xff0c;其中包含 8B 和 70B 两个版本。Llama 3 以强大的性能和丰富的功能成为迄今为止最强大的开源大语言模型之一。从已经释放的信息来看&#xff0c;Llama 3 在模型架构、训练数据、训练规模和指令微调等方面进行了多项改进&#xff0c;使其在推理、…

详解Java中的五种IO模型

文章目录 前言1、内核空间和用户空间2、用户态和内核态3、上下文切换4、虚拟内存5、DMA技术6、传统 IO 的执行流程 一、阻塞IO模型二、非阻塞IO模型三、IO多路复用模型1、IO多路复用之select2、IO多路复用之epoll3、总结select、poll、epoll的区别 四、IO模型之信号驱动模型五、…

Git 原理及使用 (带动图演示)

文章目录 &#x1f308; Ⅰ Git 安装&#x1f319; 01. Linux - centos &#x1f308; Ⅱ Git 工作区、暂存区和版本库&#x1f319; 01. 认识工作区、暂存区和版本库&#x1f319; 02. 使用 Git 管理工作区的文件 &#x1f308; Ⅲ Git 基本操作&#x1f319; 01. 创建本地仓库…

Java代码基础算法练习-斐波纳契数列-2024.04.22

任务描述&#xff1a; 1&#xff0c;1&#xff0c;2&#xff0c;3&#xff0c;5&#xff0c;8&#xff0c;13&#xff0c;21&#xff0c;34&#xff0c;55&#xff0c;89……这个数列则称为“斐波那契数列”&#xff0c;其中每 个数字都是“斐波那契数”。 输入一个整数N(N不大…

服务器渲染技术(JSPELJSTL)

目录 前言 一.JSP 1.基本介绍 3.page指令(常用) 4.JSP三种常用脚本 4.1 声明脚本 <%! code %> 4.2 表达式脚本 <% code %> 4.3 代码脚本 <% code %> 4.4 注释 <%-- 注释 --%> 5. JSP 内置对象 5.1 基本介绍 5.2 九个内置对象 6.JSP域对象 二…

4-内核开发-第一个块设备模块开发案例

4-内核开发-第一个块设备模块开发案例 目录 4-内核开发-第一个块设备模块开发案例 1.开发原则创建步骤 2. 编译并加载模块 ​3.安装模块 4.检查模块是否加载成功 5.通过设备名称查看 6. 创建一个块设备文件 7. 查看块设备 8.模块卸载 9.总结 课程简介&#xff1a; L…

Qt-饼图示范

1.效果图 2.代码如下 2.1 .h文件 #ifndef PIECHARTWIDGET_H #define PIECHARTWIDGET_H#include <QWidget> #include <QChartView> #include <QPieSeries>#include<QVBoxLayout> #include<QMessageBox> #include <QtCharts>struct PieDat…

电子印章盖骑缝章

电子印章盖骑缝章是指在电子文档&#xff08;如PDF文件&#xff09;中&#xff0c;使用电子印章技术&#xff0c;为文档添加一个跨越多页、连续显示的电子印章图像&#xff0c;以模拟传统纸质文档上的骑缝章效果。以下是实现电子印章盖骑缝章的步骤&#xff1a; 一. 准备电子印…

linux休眠唤醒流程,及示例分析

休眠流程 应用层通过echo mem > /sys/power/state写入休眠状态&#xff0c;给一张大概流程图 这个操作对应在kernel/power/main.c的state这个attr的store操作 static ssize_t state_store(struct kobject *kobj, struct kobj_attribute *attr,const char *buf, size_t n) …

Linux - Docker 安装 Nacos

拉取 Nacos 镜像 使用以下命令从 Docker Hub 拉取最新版本的 Nacos 镜像&#xff1a; docker pull nacos/nacos-server启动 Nacos 容器 使用以下命令启动 Nacos 容器&#xff1a; docker run -d \--name nacos \--privileged \--cgroupns host \--env JVM_XMX256m \--env M…

【Harmony3.1/4.0】笔记三

概念 网格布局是由“行”和“列”分割的单元格所组成&#xff0c;通过指定“项目”所在的单元格做出各种各样的布局。网格布局具有较强的页面均分能力&#xff0c;子组件占比控制能力&#xff0c;是一种重要自适应布局&#xff0c;其使用场景有九宫格图片展示、日历、计算器等…

Vue2 —— 学习(十)

一、vue-resource 库 了解即可 在之前的 vue 版本中经常使用 这个库发送 ajax 请求 现在建议使用 axios 我们可以通过使用 vue-resource 库 来实现发送 ajax 请求 它是 vue 的一个插件库 Vue.use() 就能使用我们的插件了 我们引入后去 我们的实例对象 vc 中查看 发现出现…