Future、CompletionService、CompletableFuture介绍与对比

2024-01-05 19:12

本文主要是介绍Future、CompletionService、CompletableFuture介绍与对比,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

目录

  • Future
    • 1、基本介绍
    • 2、按照提交任务的顺序获取执行结果
  • CompletionService
    • 1、介绍
    • 2、按照任务完成的先后顺序获取结果
  • CompletableFuture
    • 1、介绍
    • 2、CompletableFuture怎么非阻塞的获取任务结果

Future

1、基本介绍

Future是JDK1.5 提供的接口,是用来以阻塞的方式获取线程异步执行完的结果。

FutureTask 类是 Java 中 Future 接口的一个实现,同时也实现了 Runnable 接口。它用于表示异步计算的结果,允许一个任务在一个线程中计算结果,在另一个线程中获取计算的结果。

import java.util.concurrent.*;public class CallableWithThreadPoolExample {public static void main(String[] args) {// 创建一个固定大小的线程池ExecutorService executorService = Executors.newFixedThreadPool(2);// 创建一个Callable任务Callable<Integer> callableTask = () -> {// 模拟耗时的计算try {Thread.sleep(2000);} catch (InterruptedException e) {e.printStackTrace();}// 返回计算结果return 42;};// 提交Callable任务给线程池执行Future<Integer> future = executorService.submit(callableTask);// 执行其他任务,不会阻塞try {// 获取Callable任务的结果,会阻塞直到结果准备好Integer result = future.get();System.out.println("Result: " + result);} catch (InterruptedException | ExecutionException e) {e.printStackTrace();} finally {// 关闭线程池executorService.shutdown();}}
}

2、按照提交任务的顺序获取执行结果

FutureTask 提供了 get 方法,可以用于获取异步计算的结果。然而,FutureTask 本身并没有保证按照任务提交的顺序返回结果。

如果你需要按照任务提交的顺序获取执行结果,你可以使用 ExecutorService 的 invokeAll 方法提交一批 Callable 任务,并得到一组 Future 对象,然后可以通过迭代这组 Future 对象来获取执行结果,迭代的顺序即为任务提交的顺序。

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.*;public class OrderPreservingExample {public static void main(String[] args) {ExecutorService executorService = Executors.newFixedThreadPool(5);List<Callable<String>> tasks = new ArrayList<>();for (int i = 0; i < 10; i++) {final int taskId = i;Callable<String> task = () -> {// Simulate some computationThread.sleep(1000);return "Task " + taskId + " completed";};tasks.add(task);}try {List<Future<String>> results = executorService.invokeAll(tasks);for (Future<String> result : results) {System.out.println(result.get()); // Results are obtained in the order of task submission}} catch (InterruptedException | ExecutionException e) {e.printStackTrace();} finally {executorService.shutdown();}}
}

在上面的示例中,invokeAll 方法会按照任务列表的顺序返回 Future 对象的列表,因此通过迭代这个列表,可以按照任务提交的顺序获取执行结果。请注意,invokeAll 方法会阻塞直到所有任务完成。

CompletionService

1、介绍

CompletionService 是 Java 中用于处理一批异步任务的工具类,它允许你以异步的方式提交任务,并在任务完成时按照完成顺序获取结果。CompletionService 的底层原理主要基于阻塞队列和线程池。

CompletionService 使用一个阻塞队列来保存已完成的任务。当一个任务完成时,它会被放入队列中。阻塞队列的选择通常是 LinkedBlockingQueue,它是一个先进先出的队列,确保按照任务完成的顺序排列。

CompletionService 通常与 Executor 框架一起使用。创建一个 ExecutorService,并将其传递给 CompletionService 的构造函数。这个线程池负责执行提交的任务。

当想要获取已完成的任务的结果时,可以调用 CompletionService 的 take() 或 poll() 方法。这些方法会从阻塞队列中取出已完成的任务的 Future,并返回它。如果队列为空,take() 方法会阻塞,而 poll() 方法会返回 null。

由于使用了阻塞队列,你可以确保按照任务完成的顺序获取结果,即使任务的完成顺序与它们被提交的顺序不同。

2、按照任务完成的先后顺序获取结果

Future 接口本身并没有直接提供按照任务提交的顺序获取执行结果的机制。当通过 ExecutorService 提交多个任务时,Future 对象的完成顺序可能不一定与任务的提交顺序完全一致。

ExecutorCompletionService 是 ExecutorService 的一个实现,它可以将已完成的任务放入一个阻塞队列中,然后可以通过检索队列的元素来按照任务完成的顺序获取结果。

当向 CompletionService 提交一个任务时,它会将该任务包装在一个 Future 中,并将这个 Future 放入阻塞队列中。

import java.util.concurrent.*;public class OrderPreservingFutureExample {public static void main(String[] args) {ExecutorService executorService = Executors.newFixedThreadPool(5);ExecutorCompletionService<String> completionService = new ExecutorCompletionService<>(executorService);for (int i = 0; i < 10; i++) {final int taskId = i;Callable<String> task = () -> {// Simulate some computationThread.sleep(1000);return "Task " + taskId + " completed";};completionService.submit(task);}try {for (int i = 0; i < 10; i++) {Future<String> result = completionService.take(); // Blocking until a task is completedSystem.out.println(result.get()); // Results are obtained in the order of completion}} catch (InterruptedException | ExecutionException e) {e.printStackTrace();} finally {executorService.shutdown();}}
}

在上面的示例中,ExecutorCompletionService 的 take 方法会阻塞直到有任务完成,然后返回一个 Future 对象,我们能够通过这个对象获取任务的执行结果。通过循环迭代 take 方法,可以按照任务完成的顺序获取执行结果。

这是因为CompletionService 的 take 方法是阻塞的。当调用 take 方法时,它会等待至少一个任务完成并返回一个包含已完成任务结果的 Future 对象。如果当前没有已完成的任务,take 方法会阻塞直到有任务完成为止。这种阻塞行为使得能够按照任务完成的顺序获取结果,因为它会返回最先完成的任务的结果。

如果我们希望非阻塞地检查是否有任务完成,可以使用 poll 方法,它会立即返回一个 Future 对象,如果没有已完成的任务,则返回 null。但使用 poll 方法可能需要在循环中进行主动轮询。

import java.util.concurrent.*;public class PollingCompletionServiceExample {public static void main(String[] args) {ExecutorService executorService = Executors.newFixedThreadPool(5);CompletionService<String> completionService = new ExecutorCompletionService<>(executorService);// 提交任务到CompletionServicefor (int i = 0; i < 5; i++) {final int taskId = i;Callable<String> task = () -> {// 模拟一些计算Thread.sleep(1000);return "Task " + taskId + " completed";};completionService.submit(task);}// 轮询任务的完成状态for (int i = 0; i < 5; i++) {Future<String> result = completionService.poll();if (result != null) {// 任务已完成,处理结果try {System.out.println(result.get());} catch (InterruptedException | ExecutionException e) {e.printStackTrace();}} else {// 没有已完成的任务System.out.println("No completed tasks at the moment.");}}executorService.shutdown();}
}

在上面的示例中,poll方法被用于轮询已完成的任务。如果有任务已经完成,它会返回一个包含任务结果的Future对象;否则,返回null。需要注意的是,如果任务尚未完成,poll方法会立即返回null,因此你可能需要在循环中添加适当的延迟,以避免过于频繁地检查任务状态。

CompletableFuture

1、介绍

Future和CompletionService 在实际使用过程中存在一些局限性比如不支持异步任务的编排组合、获取计算结果的 get() /take()为阻塞调用。

Java 8 才被引入CompletableFuture 类可以解决Future 的这些缺陷。CompletableFuture 除了提供了更为好用和强大的 Future 特性之外,还提供了函数式编程、异步任务编排组合(可以将多个异步任务串联起来,组成一个完整的链式调用)等能力。

CompletableFuture 同时实现了 Future 和 CompletionStage 接口。

CompletionStage 接口描述了一个异步计算的阶段。很多计算可以分成多个阶段或步骤,此时可以通过它将所有步骤组合起来,形成异步计算的流水线。

CompletableFuture 除了提供了更为好用和强大的 Future 特性之外,还提供了函数式编程的能力。

2、CompletableFuture怎么非阻塞的获取任务结果

通过 thenApply, thenAccept, 或者 thenRun 方法,注册回调函数,这些函数会在 CompletableFuture 完成时被异步调用。这样,处理任务的结果而不必阻塞当前线程。

CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> "Hello");future.thenApply(result -> {// Non-blocking callback to process the resultSystem.out.println("Received result: " + result);return result.toUpperCase();
});// Continue with other non-blocking operations

使用 thenCombine, thenAcceptBoth, runAfterBoth, applyToEither, acceptEither, 等方法,将多个 CompletableFuture 的结果组合在一起,而不必阻塞等待每个任务的完成。


CompletableFuture<String> firstTask = CompletableFuture.supplyAsync(() -> {// Simulate some computationreturn "First Task";
});CompletableFuture<String> secondTask = CompletableFuture.supplyAsync(() -> {// Simulate some computationreturn "Second Task";
});CompletableFuture<String> thirdTask = CompletableFuture.supplyAsync(() -> {// Simulate some computationreturn "Third Task";
});// 使用thenCompose确保任务按照顺序完成
CompletableFuture<String> result = firstTask.thenCompose(result1 ->secondTask.thenCompose(result2 ->thirdTask.thenApply(result3 -> result1 + " -> " + result2 + " -> " + result3))
);// 异步获取结果
result.thenAcceptAsync(System.out::println);// 阻塞等待所有任务完成
result.join();

这篇关于Future、CompletionService、CompletableFuture介绍与对比的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/573881

相关文章

Java CompletableFuture如何实现超时功能

《JavaCompletableFuture如何实现超时功能》:本文主要介绍实现超时功能的基本思路以及CompletableFuture(之后简称CF)是如何通过代码实现超时功能的,需要的... 目录基本思路CompletableFuture 的实现1. 基本实现流程2. 静态条件分析3. 内存泄露 bug

四种Flutter子页面向父组件传递数据的方法介绍

《四种Flutter子页面向父组件传递数据的方法介绍》在Flutter中,如果父组件需要调用子组件的方法,可以通过常用的四种方式实现,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录方法 1:使用 GlobalKey 和 State 调用子组件方法方法 2:通过回调函数(Callb

Python进阶之Excel基本操作介绍

《Python进阶之Excel基本操作介绍》在现实中,很多工作都需要与数据打交道,Excel作为常用的数据处理工具,一直备受人们的青睐,本文主要为大家介绍了一些Python中Excel的基本操作,希望... 目录概述写入使用 xlwt使用 XlsxWriter读取修改概述在现实中,很多工作都需要与数据打交

java脚本使用不同版本jdk的说明介绍

《java脚本使用不同版本jdk的说明介绍》本文介绍了在Java中执行JavaScript脚本的几种方式,包括使用ScriptEngine、Nashorn和GraalVM,ScriptEngine适用... 目录Java脚本使用不同版本jdk的说明1.使用ScriptEngine执行javascript2.

Python实现NLP的完整流程介绍

《Python实现NLP的完整流程介绍》这篇文章主要为大家详细介绍了Python实现NLP的完整流程,文中的示例代码讲解详细,具有一定的借鉴价值,感兴趣的小伙伴可以跟随小编一起学习一下... 目录1. 编程安装和导入必要的库2. 文本数据准备3. 文本预处理3.1 小写化3.2 分词(Tokenizatio

锐捷和腾达哪个好? 两个品牌路由器对比分析

《锐捷和腾达哪个好?两个品牌路由器对比分析》在选择路由器时,Tenda和锐捷都是备受关注的品牌,各自有独特的产品特点和市场定位,选择哪个品牌的路由器更合适,实际上取决于你的具体需求和使用场景,我们从... 在选购路由器时,锐捷和腾达都是市场上备受关注的品牌,但它们的定位和特点却有所不同。锐捷更偏向企业级和专

什么是 Ubuntu LTS?Ubuntu LTS和普通版本区别对比

《什么是UbuntuLTS?UbuntuLTS和普通版本区别对比》UbuntuLTS是Ubuntu操作系统的一个特殊版本,旨在提供更长时间的支持和稳定性,与常规的Ubuntu版本相比,LTS版... 如果你正打算安装 Ubuntu 系统,可能会被「LTS 版本」和「普通版本」给搞得一头雾水吧?尤其是对于刚入

TP-LINK/水星和hasivo交换机怎么选? 三款网管交换机系统功能对比

《TP-LINK/水星和hasivo交换机怎么选?三款网管交换机系统功能对比》今天选了三款都是”8+1″的2.5G网管交换机,分别是TP-LINK水星和hasivo交换机,该怎么选呢?这些交换机功... TP-LINK、水星和hasivo这三台交换机都是”8+1″的2.5G网管交换机,我手里的China编程has

性能测试介绍

性能测试是一种测试方法,旨在评估系统、应用程序或组件在现实场景中的性能表现和可靠性。它通常用于衡量系统在不同负载条件下的响应时间、吞吐量、资源利用率、稳定性和可扩展性等关键指标。 为什么要进行性能测试 通过性能测试,可以确定系统是否能够满足预期的性能要求,找出性能瓶颈和潜在的问题,并进行优化和调整。 发现性能瓶颈:性能测试可以帮助发现系统的性能瓶颈,即系统在高负载或高并发情况下可能出现的问题

水位雨量在线监测系统概述及应用介绍

在当今社会,随着科技的飞速发展,各种智能监测系统已成为保障公共安全、促进资源管理和环境保护的重要工具。其中,水位雨量在线监测系统作为自然灾害预警、水资源管理及水利工程运行的关键技术,其重要性不言而喻。 一、水位雨量在线监测系统的基本原理 水位雨量在线监测系统主要由数据采集单元、数据传输网络、数据处理中心及用户终端四大部分构成,形成了一个完整的闭环系统。 数据采集单元:这是系统的“眼睛”,