当前位置: 首页 > news >正文

Future<V>接口 和 CompletableFuture<T>类 介绍


Future接口与CompletableFuture类

  • Future<V>接口
    • Future 接口代码
    • Future使用场景
    • Future使用代码示例
    • Future鸡肋说明(为何引入了CompletableFuture)
  • FutureTask<V>类
  • CompletableFuture<T>类
    • CompletableFuture类代码
    • CompletableFuture使用示例
      • CompletableFuture的创建方式
      • CompletableFuture回调函数
      • CompletableFuture链式调用
      • CompletableFuture非阻塞获取结果(支持回调函数)
      • CompletableFuture异常处理
      • CompletableFuture组合任务
      • CompletableFuture手动任务

Future接口

在API文档中描述大意是,用于表示异步计算的结果。它提供了一种机制,允许我们在任务提交后获取任务的执行状态和结果。Future 通常与 ExecutorService线程池 结合使用,用于管理异步任务的执行
这个我们先理解同步异步的区分

  • 同步:发送一个请求,等待返回,然后再发送下一个请求
  • 异步:发送一个请求,不等待返回,随时可以再发送下一个请求

同步和异步最大的区别就在于。一个需要等待,一个不需要等待

  • 同步例子:电话,发起者需要等待接收者,接通电话后,通信才开始。需要等待接收者的返回信息
  • 异步例子:广播,发起者不关心接收者的状态。不需要等待接收者的返回信息

Future 接口代码

public interface Future<V> {// 尝试取消任务。如果任务正在运行,mayInterruptIfRunning 决定是否中断任务boolean cancel(boolean mayInterruptIfRunning);//检查任务是否被取消boolean isCancelled();//检查任务是否完成(正常完成、取消或异常)boolean isDone();//获取任务结果(阻塞直到任务完成)V get() throws InterruptedException, ExecutionException;//获取任务结果(最多等待指定时间)V get(long timeout, TimeUnit unit)throws InterruptedException, ExecutionException, TimeoutException;
}

Future使用场景

1.异步任务执行:提交任务后,继续执行其他逻辑,稍后获取任务结果
2.任务状态监控:检查任务是否完成、是否被取消。
3.任务取消:在任务完成前取消任务

Future使用代码示例

    @Testpublic void Test() throws  InterruptedException, ExecutionException {// 创建线程池ExecutorService executorService = Executors.newSingleThreadExecutor();// 提交 Callable 任务,返回 Future 对象Future<Integer> future = executorService.submit(()->{Thread.sleep(100);return 500;});// 检查任务是否完成System.out.println("Task is done: " + future.isDone());// 获取任务结果(阻塞直到任务完成)Integer result = future.get();System.out.println("Task result: " + result);// 再次检查任务状态System.out.println("Task is done: " + future.isDone());// 关闭线程池executorService.shutdown();}

Future鸡肋说明(为何引入了CompletableFuture)

虽然Future官方定义是异步的机制,但Future并不能实现真正的异步
1.如Future.get()方法获取的是任务状态和结果,但需要阻塞获取结果,很好理解要结果需要等到任务结束才有结果,它要等任务完成了才会去执行get方法,get方法从等待到执行就是一个阻塞过程

阻塞定义是:指的是线程能够运行,但是某个条件阻止它的运行,当线程处于阻塞状态时,调度器将忽略线程,不会分配给线程任何CPU时间,直到线程重新进入就绪状态,它才有可能执行操作

2.又或者boolean isDone();boolean isCancelled();这两个方法需要我们自定义执行去查询从而获取到任务的状态,这样也不算异步
所以有了更强大的 CompletableFuture类实现真正的异步,以下开始介绍 CompletableFuture类 以及怎么实现异步的

FutureTask类

FutureTask类 算是Future接口的一个实现类,通过实现RunnableFuture接口,而RunnableFuture接口又继承了Runnable和Future接口,所以它拥有实现Runnable, Future两个接口的功能,及多线程实现和异步计算结果获取功能,算个杂交体吧

public class FutureTask<V> implements RunnableFuture<V> {
}public interface RunnableFuture<V> extends Runnable, Future<V> {void run();
}

它的方法和Future接口的方法大体一致,获取Callable 任务返回结果或异常区别在于它又Runnale接口的功能,即它本身可以作为一个线程任务可以用start方式启动执行FutureTask,也可以想Future一样交给线程池启动执行

FutureTask<Integer> futureTask = new FutureTask<>(new Callable<Integer>() {@Overridepublic Integer call() throws Exception {// 异步任务逻辑return 42;}
});// 方法一:直接启动线程
new Thread(futureTask).start();// 方法二:使用ExecutorService
ExecutorService executorService = Executors.newSingleThreadExecutor();
executorService.submit(futureTask);try {Integer result = futureTask.get(); // 阻塞直到任务完成System.out.println("Result: " + result);
} catch (InterruptedException | ExecutionException e) {e.printStackTrace();
}

CompletableFuture类

它是对 Future 的增强类,实现Future 接口并新增许多方法,提供了更强大的功能,如 非阻塞获取结果回调函数链式调用异常处理组合任务手动完成任务

CompletableFuture类代码

public class CompletableFuture<T> implements Future<T>, CompletionStage<T> {//获取结果get();//阻塞获取结果。join();//阻塞获取结果,但不抛出受检异常。getNow(T valueIfAbsent);//立即获取结果,如果任务未完成,返回默认值。//回调函数thenApply();//任务完成后执行回调,返回新的结果。thenAccept();//任务完成后执行回调,消费结果。thenRun();//任务完成后执行回调,不消费结果。//异常处理exceptionally();//处理任务抛出的异常。handle();//无论任务是否成功,都会执行回调。//组合任务thenCompose();//将两个任务串联执行。thenCombine();//将两个任务并联执行,并合并结果。allOf();//等待所有任务完成。anyOf();//等待任意一个任务完成//手动任务complete("Manual result"); // 手动设置结果
}

CompletableFuture使用示例

CompletableFuture的创建方式

1.构造器方式 2.类带有的静态工厂方法

    @Testpublic void test(){//创建CompletableFuture构造方法创建CompletableFuture实例对象CompletableFuture<String> completableFuture = new CompletableFuture<String>();completableFuture.complete("构造方法创建实例 手动设置结果");// 以下三种 使用CompletableFuture类带有的静态工厂方法CompletableFuture<String> future  = CompletableFuture.completedFuture("通过类自带静态方法创建一个");CompletableFuture<Void> future1 = completableFuture.runAsync(()->{System.out.println("创建一个异步任务但不需要返回结果");});CompletableFuture<String> future2 = CompletableFuture.supplyAsync(()->{return "创建一个异步任务并返回结果";});}

CompletableFuture回调函数

这里先引入回调函数概念与示例
回调函数(Callback Function):是指使用者自己定义一个函数,然后把这个函数作为参数传入别的函数中,由别的函数在运行时来调用的函数。函数是你实现的,但由别的函数在运行时通过参数传递的方式调用,这就是所谓的回调函数。
简单来说,就是由别人的函数运行期间来回调你实现的函数
示例

	//①定义一个具有返回值String类型的方法Apublic String A(){return "a";}//②再定义一个具有String类型参数的方法Bpublic void B(String str){}public static void main(String[] args) {SystemTest st = new SystemTest();//③将A方法的返回值作为参数放在方法B中,这样调用方法B时,会调用方法A 这个过程就是回调st.B(st.A());  }

再看下 CompletableFuture涉及到的回调函数具体使用方式
1.thenApply()
任务完成后执行回调,返回新的结果

    @Testpublic void test() throws ExecutionException, InterruptedException {CompletableFuture<String> c = CompletableFuture.supplyAsync(()-> "hello").thenApply(resultData->resultData+"world");System.out.println(c.get());}

①首先创建了一个CompletableFuture对象,并使用supplyAsync方法异步执行一个任务,该任务返回字符串"hello"
②我们使用thenApply方法对这个结果回调,进行处理,将其与字符串"world"拼接起来。
③我们通过get方法获取最终的结果并打印出来
2.thenAccept()
任务完成后执行回调,消费结果

    @Testpublic void test() throws ExecutionException, InterruptedException {CompletableFuture<Void> c = CompletableFuture.supplyAsync(()-> "hello").thenAccept(System.out::println);}

输出内容hello
3.thenRun()
任务完成后执行回调,不消费结果

    @Testpublic void test() throws ExecutionException, InterruptedException {CompletableFuture<Void> c = CompletableFuture.supplyAsync(()-> "hello").thenRun(System.out::println);}

打印台为空,没有打印内容

CompletableFuture链式调用

链式调用的概念:通过在方法调用后返回对象本身,实现对方法的连续调用

public class Calculator {private int result;public Calculator() {this.result = 0;}public Calculator add(int num) {this.result += num;return this;}public Calculator subtract(int num) {this.result -= num;return this;}public Calculator multiply(int num) {this.result *= num;return this;}public Calculator divide(int num) {if (num != 0) {this.result /= num;}return this;}public int getResult() {return this.result;}public static void main(String[] args) {Calculator calculator = new Calculator();int result = calculator.add(5).subtract(3).multiply(4).divide(2).getResult();System.out.println(result); // 输出:8}
}

Calculator类实现了四个基本的数学运算方法:add、subtract、multiply和divide。每个方法都返回this,即当前对象,以实现链式调用。通过这种方式,可以在一个表达式中连接多个方法,使代码更加流畅和易于理解

CompletableFuture可以使用多个方法串联使用实现链式调用

public class CompletableFutureChainExample {public static void main(String[] args) {CompletableFuture.supplyAsync(() -> "Hello").thenApply(s -> s + ", World") // 串联任务.thenAccept(System.out::println); // 消费结果}
}

CompletableFuture非阻塞获取结果(支持回调函数)

1.获取结果基本用法

    @Testpublic void test() throws ExecutionException, InterruptedException {CompletableFuture<String> future2 = CompletableFuture.supplyAsync(()->{return "创建一个异步任务并返回结果";});System.out.println(future2.get());}

这个示例上get方法想要执行获取结果 需要task任务完成就是future2完成supplyAsync方法返回结果示例,get方法从等待执行到执行过程就是阻塞过程
2.非阻塞获取结果
那CompletableFuture是如何非阻塞获取结果的的呢,这就涉及到上面写的CompletableFuture有回调函数和链式调用的设计了

CompletableFuture回调函数有thenApply ,thenAccpt,Handle等,已thenApply为例说明

thenApply 是 CompletableFuture 中的一个方法,用于在前一个 CompletableFuture 完成之后应用一个函数,并返回一个新的 CompletableFuture。它的核心特点是 异步链式调用,即前一个任务完成后,自动触发后续任务
1.前一个任务完成后
thenApply 会自动触发,并将前一个任务的结果作为输入,应用指定的函数,返回一个新的 CompletableFuture

public class ThenApplyExample {public static void main(String[] args) throws ExecutionException, InterruptedException {// 创建一个未完成的 CompletableFutureCompletableFuture<String> future = new CompletableFuture<>();// 注册 thenApply 回调CompletableFuture<String> transformedFuture = future.thenApply(result -> {System.out.println("Applying transformation...");return result.toUpperCase(); // 将结果转换为大写});// 手动完成前一个任务future.complete("hello");// 获取转换后的结果String result = transformedFuture.get();System.out.println("Transformed result: " + result);}
}

输出内容

Applying transformation...
Transformed result: HELLO

2.前一个任务未完成
thenApply 不会立即执行,而是将函数注册为回调,等待前一个任务完成后再执行

public class ThenApplyPendingExample {public static void main(String[] args) {// 创建一个未完成的 CompletableFutureCompletableFuture<String> future = new CompletableFuture<>();// 注册 thenApply 回调CompletableFuture<String> transformedFuture = future.thenApply(result -> {System.out.println("Applying transformation...");return result.toUpperCase(); // 将结果转换为大写});// 不手动完成任务System.out.println("Main thread continues...");}
}

输出内容

Main thread continues...

thenApply会根据前一个任务是否完成而决定是否触发,本质上就是前一个完成作为参数传入该方法然后执行,如果前一个未完成就意味着没有传入参数就不会触发执行
总结:
1.thenApply是非阻塞方法 ,调用 thenApply 时会立即返回一个新的 CompletableFuture,而不会阻塞当前线程,获取过程没有阻塞,我们不用等任务结束去get,自动触发执行,这就是非阻塞获取结果,但是如果前一个未完成,等待前一个完成再去触发 thenApply方法执行,这个过程是阻塞的,但这是thenApply方法执行之前阻塞和thenApply方法无关,也就是 获取结果是否阻塞 无关
2.thenApply通常与 supplyAsync() 等异步任务结合使用 , supplyAsync() 是在创建实例就返回值,速度很快的
3.支持链式调用,可以串联多个 thenApply

CompletableFuture异常处理

1.链式调用exceptionally方法

    @Testpublic void test(){CompletableFuture.supplyAsync(()->{throw new RuntimeException("Task failed");}).exceptionally(ex->{System.out.println("Exception: " + ex.getMessage());return "fail";}).thenAccept(System.out::println);}

打印内容

Exception: java.lang.RuntimeException: Task failed
fail

1.链式调用handle方法
方法参数handle(BiFunction<? super T, Throwable, ? extends U> fn)

    public void test2(){CompletableFuture.supplyAsync(() -> {// 模拟可能抛出异常的操作throw new RuntimeException("Exception occurred");}).handle((result, exception) -> {//处理计算结果或者异常情况if (exception != null) {//计算中判断是否出现异常// andle the exception 处理异常情况System.out.println("Exception handled: " + exception.getMessage());return "Default value"; // 返回默认值} else {// handle the result处理正常结果return result;}}).thenAccept(System.out::println);}

1.supplyAsync创建了一个CompletableFuture,并在其计算过程中抛出了一个异常。
2.handle方法接收一个BiFunction,用于处理计算结果或者异常情况。如果计算中出现异常,BiFunction的第二个参数(上面示例参数exception)会包含该异常信息,否则该参数为null。
3.在这个例子中,我们判断如果出现异常,会打印异常信息,并返回一个默认值。否则返回结果

CompletableFuture组合任务

thenCompose()方法 将两个任务串联执行
用于将一个CompletableFuture实例转化为另一个CompletableFuture实例。它接收一个Function参数,该参数将前一个CompletableFuture返回的结果作为输入,并返回一个新的CompletableFuture对象

    @Testpublic void test(){CompletableFuture<String> future1 = CompletableFuture.supplyAsync(()->"hello");CompletableFuture<Void> future2 = future1.thenCompose(s -> {// 将前一个任务的结果作为参数传递给下一个任务return CompletableFuture.supplyAsync(() -> s + " World");}).thenAccept(System.out::println);}

thenCombine()方法 将两个任务并联执行,并合并结果
用于将两个CompletableFuture实例合并为一个。它接收另一个CompletableFuture实例和一个BiFunction参数(BiFunction是一个函数式接口,接受两个输入值并返回一个结果),BiFunction参数参数将两个CompletableFuture返回的结果作为输入,并返回一个新的CompletableFuture对象3。thenCombine()会在两个任务都执行完成后,把两个任务的结果合并

    @Testpublic void test(){CompletableFuture<String> future1 = CompletableFuture.supplyAsync(()->"hello");CompletableFuture<String> future2 = CompletableFuture.supplyAsync(()->"World");future1.thenCombine(future2, (s1,s2)  -> s1+","+ s2).thenAccept(System.out::println);}

总的来说,thenCompose()适用于对结果进行连续变换和处理的异步操作,而thenCombine()适用于将两个独立的异步操作的结果合并

CompletableFuture手动任务

allOf();//等待所有任务完成

public class CompletableFutureAllOfExample {public static void main(String[] args) {CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> "Task 1");CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> "Task 2");CompletableFuture<String> future3 = CompletableFuture.supplyAsync(() -> "Task 3");// 等待所有任务完成CompletableFuture.allOf(future1, future2, future3).thenRun(() -> System.out.println("All tasks completed"));}
}

http://www.mrgr.cn/news/94162.html

相关文章:

  • BLDC直流无刷电机转速电流双闭环调速MATLAB仿真
  • 21 | 全面测试项目功能
  • 12 | 给应用添加优雅关停功能
  • 完整项目案例:基于Django的毕业设计选题管理系统(包含源码结构、核心代码及设计文档框架)
  • 【Linux】在VMWare中安装Ubuntu操作系统(2025最新_Ubuntu 24.04.2)#VMware安装Ubuntu实战分享#
  • Node-RED基础1
  • 【Godot4.3】RenderingServer总结
  • Quantum Computing:量子计算如何改变世界
  • 深入探索 Java Stream
  • 使用 OptiSLang 和 MotorCAD 构建一个强大的电机优化元模型
  • 感觉自己邮电部诗人
  • 【医院成本核算专题】8.大数据与医院成本核算的关联点:开启医疗成本管理新时代
  • 使用Dockerfile构建一个Docker镜像
  • 20 | 如何添加单元测试用例
  • 计算机:基于深度学习的Web应用安全漏洞检测与扫描
  • 工作记录 2017-01-06
  • 重要!!! 改进 梯度方差(Fisher 信息近似) 指数移动平均
  • 记录一下返修
  • 【操作系统】Linux基本命令2
  • JAVA SE 4.Java各版本特性