【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,一经查实,立即删除!

相关文章

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;分别称为左子节点和…

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;也可以解压完再复制…

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

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

pytorch学习(十一)checkpoint

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

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

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

Bubbliiiing 的 Retinaface rknn python推理分析

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

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

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

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

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

手持式气象站:便携科技,掌握微观气象的利器

手持式气象站&#xff0c;顾名思义&#xff0c;是一种可以随身携带的气象监测设备。它小巧轻便&#xff0c;通常配备有温度、湿度、风速、风向、气压等多种传感器&#xff0c;能够实时测量并显示各种气象参数。不仅如此&#xff0c;它还具有数据存储、数据传输、远程控制等多种…

Django教程(003):orm操作数据库

文章目录 1 orm连接Mysql1.1 安装第三方模块1.2 ORM1.2.1、创建数据库1.2.2、Django连接数据库1.2.3、django操作表1.2.4、创建和修改表结构1.2.5、增删改查1.2.5.1 增加数据1.2.5.2 删除数据1.2.5.3 获取数据1.2.5.4 修改数据 1 orm连接Mysql Django为了使操作数据库更加简单…

Linux shell编程学习笔记65: nice命令 显示和调整进程优先级

0 前言 我们前面学习了Linux命令ps和top&#xff0c;命令的返回信息中包括优先序&#xff08;NI&#xff0c;nice&#xff09; &#xff0c;我们可以使用nice命令来设置进程优先级。 1 nice命令 的功能、格式和选项说明 1.1 nice命令 的功能 nice命令的功能是用于调整进程的…

AP ERP与汉得SRM系统集成案例(制药行业)

一、项目环境 江西某医药集团公司&#xff0c;是一家以医药产业为主营、资本经营为平台的大型民营企业集团。公司成立迄今&#xff0c;企业经营一直呈现稳健、快速发展的态势&#xff0c; 2008 年排名中国医药百强企业前 20 强&#xff0c;2009年集团总销售额约38亿元人民币…

原码、补码、反码、移码是什么?

计算机很多术语翻译成中文之后&#xff0c;不知道是译者出于什么目的&#xff0c;往往将其翻译成一个很难懂的名词。 奇怪的数学定义 下面是关于原码的“吐槽”&#xff0c;可以当作扩展。你可以不看&#xff0c;直接去下一章&#xff0c;没有任何影响。 原码的吐槽放在前面是…

配置单区域OSPF

目录 引言 一、搭建基础网络 1.1 配置网络拓扑图如下 1.2 IP地址表 二、测试每个网段都能单独连通 2.1 PC0 ping通Router1所有接口 2.2 PC1 ping通Router1所有接口 2.3 PC2 ping通Router2所有接口 2.4 PC3 ping通Router2所有接口 2.5 PC4 ping通Router3所有接口 2.…

Git仓库拆分和Merge

1. 问题背景 我们原先有一个项目叫open-api&#xff0c;后来想要做租户独立发展&#xff0c;每个租户独立成一个项目&#xff0c;比如租户akc独立部署一个akc-open-api&#xff0c;租户yhd独立部署一个yhd-open-api&#xff0c;其中大部分代码是相同的&#xff0c;少量租户定制…

2024牛客暑期多校训练营1——A,B

题解&#xff1a; 更新&#xff1a; k1的时候要乘n 代码&#xff1a; #include<bits/stdc.h> #define int long long using namespace std; const int N5e35; typedef long long ll; typedef pair<int,int> PII; int T; int n,m,mod; int fac[N][N]; int dp[N][…

笔记:Few-Shot Learning小样本分类问题 + 孪生网络 + 预训练与微调

内容摘自王老师的B站视频&#xff0c;大家还是尽量去看视频&#xff0c;老师讲的特别好&#xff0c;不到一小时的时间就缕清了小样本学习的基础知识点~Few-Shot Learning (1/3): 基本概念_哔哩哔哩_bilibili Few-Shot Learning&#xff08;小样本分类&#xff09; 假设现在每类…

【Linux】基础I/O——动静态库的制作

我想把我写的头文件和源文件给别人用 1.把源代码直接给他2.把我们的源代码想办法打包为库 1.制作静态库 1.1.制作静态库的过程 我们先看看怎么制作静态库的&#xff01; makefile 所谓制作静态库 需要将所有的.c源文件都编译为(.o)目标文件。使用ar指令将所有目标文件打包…