【JAVA多线程】Future,专为异步编程而生

目录

1.Future

2.CompletableFuture

2.1.为什么会有CompletableFuture?

2.2.使用

2.2.1.提交任务+获取结果

2.2.2.回调函数

2.2.3.CompletableFuture嵌套问题


1.Future

Java中的Future接口代表一个异步计算。其提供了一组规范用来对异步计算任务进行管理控制。

  • V get(): 阻塞等待计算完成,然后返回结果。如果计算抛出了异常,则此方法将重新抛出该异常。它有两个重载版本,区别是是否允许设置阻塞超时的时间。

  • boolean isDone(): 返回true如果任务已完成(无论是否成功),否则返回false。这包括正常完成、被取消或执行时抛出异常的情况。

  • boolean cancel(boolean mayInterruptIfRunning): 尝试取消任务的执行。如果任务尚未开始,它将被取消;如果任务正在运行且mayInterruptIfRunning为true,则执行该任务的线程将被中断;如果任务已经完成,取消请求将被忽略。此方法返回true表示任务已被取消,无论是之前已取消还是由于此次调用而取消。

  • boolean isCancelled(): 如果任务在正常完成前被取消,则返回true。

代码示例:

下面为了演示的全面一点,会把经常用到的api都调用一遍,其实直接get就能去拿值了。isDone和isCancelled只是为了保险起见而已。

import java.util.concurrent.*;
​
public class FutureAPIDemo {
​public static void main(String[] args) {ExecutorService executor = Executors.newFixedThreadPool(1);
​Future<String> future = executor.submit(() -> {Thread.sleep(2000); // 模拟耗时操作return "Hello from Future!";});
​// 检查任务是否完成while (!future.isDone()) {System.out.println("Task is not done yet...");try {Thread.sleep(500); // 让主线程等待一段时间} catch (InterruptedException e) {Thread.currentThread().interrupt();break;}}
​// 尝试获取结果if (!future.isCancelled()) {try {String result = future.get(); // 这里会阻塞直到获取到结果System.out.println("Result: " + result);} catch (InterruptedException | ExecutionException e) {System.err.println("Error getting result: " + e.getMessage());}} else {System.out.println("Task was cancelled.");}
​// 尝试取消任务(实际在这个例子中不会改变状态,因为任务已经完成)boolean cancellationResult = future.cancel(true);System.out.println("Cancellation attempt result: " + cancellationResult);
​executor.shutdown();}
}

Future接口只规定了规范,具体实现是什么样子的喃?Future怎么就能去控制异步任务了?我们具体选一个实现类来看看,可以看到JDK种带了很多Future的实现:

我们选FutureTask:

public class FutureTask<V> implements RunnableFuture<V>

可以看到FutureTask其实就是一条线程:

其实异步任务本质上就是一条线程脱离主线程另起炉灶去跑一个方法逻辑。

public interface RunnableFuture<V> extends Runnable, Future<V>

然后用一个函数式接口指向业务逻辑,有很多状态字段,通过状态去控制线程以及整个异步任务的退出和任务获取等,以get方法为例:

public class FutureTask<V> implements RunnableFuture<V> {private volatile int state;private static final int NEW          = 0;private static final int COMPLETING   = 1;private static final int NORMAL       = 2;private static final int EXCEPTIONAL  = 3;private static final int CANCELLED    = 4;private static final int INTERRUPTING = 5;private static final int INTERRUPTED  = 6;
​private Callable<V> callable;private Object outcome; // non-volatile, protected by state reads/writesprivate volatile Thread runner;private volatile WaitNode waiters;
​public V get(long timeout, TimeUnit unit)throws InterruptedException, ExecutionException, TimeoutException {if (unit == null)throw new NullPointerException();int s = state;if (s <= COMPLETING &&(s = awaitDone(true, unit.toNanos(timeout))) <= COMPLETING)throw new TimeoutException();return report(s);}......
}

2.CompletableFuture

2.1.为什么会有CompletableFuture?

future只是实现了基础的异步编程而已,但是其性能仍然可以优化,其使用上还可以扩展能多能力,completableFuture可以理解为future的升级版本:

  • CompletableFuture 提供了一系列方法,如thenApply, thenCompose, thenCombine等,允许你轻松地组合多个异步操作,构建复杂的异步工作流。相比之下,Future仅提供了获取结果或取消任务的基本功能。
  • CompletableFuture 支持注册回调函数,当异步操作完成时自动执行这些函数,无需显式调用get()方法阻塞主线程。这使得代码更易于编写和理解,避免了“回调地狱”。
  • CompletableFuture 内部使用了ForkJoinPool或其他线程池,这通常比手动管理线程更高效。此外,CompletableFuture的实现考虑了并发场景下的性能优化。

2.2.使用

2.2.1.提交任务+获取结果

CompletableFuture支持两种提交任务的方式:

  • runAsync

  • supplyAsync

两者的区别是前者没有返回值,后者有返回值。

不管是runAsync也好,还是supplyAsync也好,他们用来接收任务的参数都是一个函数式接口,这意味着什么喃?意味着可以直接通过lambda表达式来定义任务。

获取结果和Future是一样的,如果没有获取到任务的结果,就会一直阻塞,直到获取到为止。

以下以runAsync为例做一个代码演示:

CompletableFuture completableFuture = CompletableFuture.runAsync(() -> {try {Thread.sleep(1000);} catch (Exception e) {e.printStackTrace();}
});
completableFuture.get();//这里会阻塞,直到任务执行完成

2.2.2.回调函数

当任务执行完成后,我们期待有后续的关联操作,就需要用上回调函数了。CompletableFuture比起Future来说用起来很方便的一点就是CompletableFuture支持回调函数。

CompletableFuture支持三种回调函数:

  • thenRun,无参无返回。

  • thenAccept,有参无返回。

  • thenApply,有参有返回。

以下是代码示例:

thenRun:

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;public class CompletableFutureExample {public static void main(String[] args) throws ExecutionException, InterruptedException {CompletableFuture<Void> future = CompletableFuture.supplyAsync(() -> {try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}System.out.println("CompletableFuture is done.");return null;}).thenRun(() -> System.out.println("Task completed."));future.get(); // 等待任务完成}
}

thenAccept:

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;public class CompletableFutureExample {public static void main(String[] args) throws ExecutionException, InterruptedException {CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}return "Hello, CompletableFuture!";}).thenAccept(result -> System.out.println("Result: " + result));future.get(); // 等待任务完成}
}

thenApply:

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;public class CompletableFutureExample {public static void main(String[] args) throws ExecutionException, InterruptedException {CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}return "Hello, CompletableFuture!";}).thenApply(result -> result.toUpperCase());System.out.println(future.get()); // 输出: HELLO, COMPLETABLEFUTURE!}
}

2.2.3.CompletableFuture嵌套问题

CompletableFuture存在嵌套问题,举个例:

以上两个函数的返回值都是一个CompletableFuture,链式调用它们就会现成一个CompletableFuture嵌套:

CompletableFuture提供了对CompletableFuture编排的API,支持对多个CompletableFuture做聚合操作,如下我们可以调用thenCompose来将多层嵌套展开:

CompletableFuture一共提供了四种对CompletableFuture进行编排的API:

  • thenCompose,对多个CompletableFuture进行链式编排。

  • thenCombine,对两个CompletableFuture进行map操作。

  • allof,将多个任务聚合成一个任务集,集合中全部任务完成后才能继续往下走。

  • anyof,将多个任务聚合成一个任务集,集合中任意一个任务完成后就能继续往下走。

以下是代码示例:

thenCompose:

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;public class CompletableFutureComposition {public static void main(String[] args) throws ExecutionException, InterruptedException {CompletableFuture<String> firstFuture = CompletableFuture.supplyAsync(() -> {try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}return "First Result";});CompletableFuture<String> secondFuture = firstFuture.thenCompose(s -> CompletableFuture.supplyAsync(() -> {try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}return s + " Second Result";}));System.out.println(secondFuture.get());}
}

thenCombine:

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;public class CompletableFutureComposition {public static void main(String[] args) throws ExecutionException, InterruptedException {CompletableFuture<String> firstFuture = CompletableFuture.supplyAsync(() -> {try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}return "First Result";});CompletableFuture<String> secondFuture = CompletableFuture.supplyAsync(() -> {try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}return "Second Result";});CompletableFuture<String> combinedFuture = firstFuture.thenCombine(secondFuture, (s1, s2) -> s1 + " " + s2);System.out.println(combinedFuture.get());}
}

allOf:

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;public class CompletableFutureComposition {public static void main(String[] args) throws ExecutionException, InterruptedException {CompletableFuture<Void> firstFuture = CompletableFuture.runAsync(() -> {try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {throw new RuntimeException(e);}System.out.println("First task done");});CompletableFuture<Void> secondFuture = CompletableFuture.runAsync(() -> {try {TimeUnit.SECONDS.sleep(2);} catch (InterruptedException e) {throw new RuntimeException(e);}System.out.println("Second task done");});CompletableFuture<Void> allFutures = CompletableFuture.allOf(firstFuture, secondFuture);allFutures.get();System.out.println("All tasks are done");}
}

anyOf:

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;public class CompletableFutureComposition {public static void main(String[] args) throws ExecutionException, InterruptedException {CompletableFuture<Void> firstFuture = CompletableFuture.runAsync(() -> {try {TimeUnit.SECONDS.sleep(3);} catch (InterruptedException e) {throw new RuntimeException(e);}System.out.println("First task done");});CompletableFuture<Void> secondFuture = CompletableFuture.runAsync(() -> {try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {throw new RuntimeException(e);}System.out.println("Second task done");});CompletableFuture<Void> anyFuture = CompletableFuture.anyOf(firstFuture, secondFuture);anyFuture.get();System.out.println("At least one task is done");}
}

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

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

相关文章

MySQL8的备份方案——增量备份(CentOS)

MySQL8的增量备份 一、安装备份工具二、备份数据三、准备恢复所需的备份数据四、 恢复备份文件 点击跳转全量(完全)备份 点击跳转差异备份 点击跳转压缩备份 一、安装备份工具 官网 下载地址 备份所用工具为percona-xtrabackup 如果下方安装工具的教程失效&#xff0c;请点击…

PACS-医学影像信息管理系统,全影像科室PACS源码,内置包括MPR、CMPR、VR等三维处理功能

PACS系统可以覆盖医院现有放射、CT、MR、核医学、超声、内镜、病理、心电等绝大部分DICOM和非DICOM检查设备&#xff0c;支持从科室级、全院机、集团医院级乃至到区域PACS的平滑扩展&#xff0c;能够与医院HIS、集成平台的有效集成和融合&#xff0c;帮助医院实现了全院医学影像…

Qt Style Sheets-使用样式表自定义 Qt 部件

使用样式表自定义 Qt 部件 在使用样式表时&#xff0c;每个小部件都被视为具有四个同心矩形的框&#xff1a;边距矩形、边框矩形、填充矩形和内容矩形。框模型对此进行了更详细的描述。 盒模型 以下是四个同心矩形在概念上的呈现方式&#xff1a; 边距超出边框。边框绘制在边…

【深入C++】二叉搜索树

文章目录 什么是二叉搜索树二叉搜索树的接口1.查找操作2.插入操作3.中序遍历4.删除操作 所有代码总结 什么是二叉搜索树 二叉搜索树&#xff08;Binary Search Tree, BST&#xff09;是一种特殊的二叉树&#xff0c;其每个节点最多有两个子节点&#xff0c;分别称为左子节点和…

C++实现数组中是否存在递增三元组的巧妙方法【奇思妙想】

C实现数组中是否存在递增三元组的巧妙方法 在解决数组问题时&#xff0c;尤其是涉及到子序列的查找&#xff0c;我们需要考虑时间复杂度和空间复杂度&#xff0c;以确保算法的效率。我们将介绍一种高效的解决方案&#xff0c;详细讲解其思路和实现。 问题描述 给你一个整数数…

Linux环境下dockes使用MongoDB,上传zip文件如何解压并备份恢复到MongoDB数据库中

1、准备 Docker 和 MongoDB 容器 建议主机端口改一下 docker run --name mongodb -d -p 27018:27017 mongo 2. 创建一个工作目录并将 zip 文件上传到dockers容器中 docker cp data.zip mongodb:/data.zip 3. 在 MongoDB 容器中解压 zip 文件&#xff08;也可以解压完再复制…

C++仿函数

在C中&#xff0c;我们经常需要对类中的元素进行比较&#xff0c;例如在排序、查找等操作中。为了使类更加灵活&#xff0c;我们可以通过自定义比较函数来实现不同的比较方式。在本文中&#xff0c;我们将探讨如何在类中使用仿函数和 Lambda 表达式来定义自定义比较函数。 1. …

《C++并发编程实战》笔记(三)

三、线程间共享数据的保护 多个线程同时访问修改共享的数据时&#xff0c;如果不加以控制&#xff0c;可能会造成未知的错误&#xff0c;为了解决这个问题&#xff0c;需要采取特殊的手段保证数据在各个线程间可以被正常使用。 这里介绍使用互斥量保护数据的方法。 3.1 使用互…

基础vrrp(虚拟路由冗余协议)

一、VRRP 虚拟路由冗余协议 比如交换机上联两个路由器&#xff0c;由两个路由虚拟出一台设备设置终端设备的网关地址&#xff0c;两台物理路由的关系是主从关系&#xff0c;可以设置自动抢占。终端设备的网关是虚拟设备的ip地址&#xff0c;这样&#xff0c;如果有一台路由设备…

pytorch学习(十一)checkpoint

当训练一个大模型数据的时候&#xff0c;中途断电就可以造成已经训练几天或者几个小时的工作白做了&#xff0c;再此训练的时候需要从epoch0开始训练&#xff0c;因此中间要不断保存&#xff08;epoch&#xff0c;net&#xff0c;optimizer&#xff0c;scheduler&#xff09;等…

深入探索:Stable Diffusion 与传统方法对比:优劣分析

深入探索&#xff1a;Stable Diffusion 与传统方法对比&#xff1a;优劣分析 一、引言 随着人工智能和深度学习的发展&#xff0c;优化算法在神经网络训练中的重要性日益凸显。传统的优化方法&#xff0c;如随机梯度下降&#xff08;SGD&#xff09;、动量法和Adam等&#xf…

动手学深度学习——5.卷积神经网络

1.卷积神经网络特征 现在&#xff0c;我们将上述想法总结一下&#xff0c;从而帮助我们设计适合于计算机视觉的神经网络架构。 平移不变性&#xff08;translation invariance&#xff09;&#xff1a;不管检测对象出现在图像中的哪个位置&#xff0c;神经网络的前面几层应该对…

《昇思 25 天学习打卡营第 15 天 | 基于MindNLP+MusicGen生成自己的个性化音乐 》

《昇思 25 天学习打卡营第 15 天 | 基于MindNLPMusicGen生成自己的个性化音乐 》 活动地址&#xff1a;https://xihe.mindspore.cn/events/mindspore-training-camp 签名&#xff1a;Sam9029 MusicGen概述 MusicGen是由Meta AI的Jade Copet等人提出的一种基于单个语言模型&…

密码学原理精解【8】

文章目录 概率分布哈夫曼编码实现julia官方文档建议的变量命名规范&#xff1a;julia源码 熵一、信息熵的定义二、信息量的概念三、信息熵的计算步骤四、信息熵的性质五、应用举例 哈夫曼编码&#xff08;Huffman Coding&#xff09;基本原理编码过程特点应用具体过程1. 排序概…

Bubbliiiing 的 Retinaface rknn python推理分析

Bubbliiiing 的 Retinaface rknn python推理分析 项目说明 使用的是Bubbliiiing的深度学习教程-Pytorch 搭建自己的Retinaface人脸检测平台的模型&#xff0c;下面是项目的Bubbliiiing视频讲解地址以及源码地址和博客地址&#xff1b; 作者的项目讲解视频&#xff1a;https:…

FFmpeg音视频流媒体的顶级项目

搞音视频、流媒体的圈子,没法躲开ffmpeg这个神级项目。 FFmpeg 是一个功能强大且广泛使用的多媒体处理工具。FFmpeg 具备众多出色的特性。它支持多种音频和视频格式的转换,能轻松将一种格式的文件转换为另一种,满足不同设备和应用的需求。不仅如此,它还可以进行视频的裁剪、…

php编译安装

一、基础环境准备 # php使用www用户 useradd -s /sbin/nologin -M www二、下载php包 # 下载地址 https://www.php.net/downloads wget https://www.php.net/distributions/php-8.3.9.tar.gz三、配置编译安装 编译安装之前需要处理必要的依赖&#xff0c;在编译配置安装&…

使用多进程和多线程实现服务器并发【C语言实现】

在TCP通信过程中&#xff0c;服务器端启动之后可以同时和多个客户端建立连接&#xff0c;并进行网络通信&#xff0c;但是在一个单进程的服务器的时候&#xff0c;提供的服务器代码却不能完成这样的需求&#xff0c;先简单的看一下之前的服务器代码的处理思路&#xff0c;再来分…

Python中自定义上下文管理器的创建与应用

Python中自定义上下文管理器的创建与应用 在Python编程中,上下文管理器是一个非常重要的概念。它们允许我们更好地管理资源,如文件句柄、网络连接或数据库连接等,确保这些资源在使用后能够被正确关闭或释放,从而避免资源泄露。Python标准库中的contextlib模块提供了强大的…

代码随想录学习 day54 图论 Bellman_ford 算法精讲

Bellman_ford 算法精讲 卡码网&#xff1a;94. 城市间货物运输 I 题目描述 某国为促进城市间经济交流&#xff0c;决定对货物运输提供补贴。共有 n 个编号为 1 到 n 的城市&#xff0c;通过道路网络连接&#xff0c;网络中的道路仅允许从某个城市单向通行到另一个城市&#xf…