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

Future,Callable,CompletableFuture是什么?

最近在复习rpc框架以及juc的时候,遇到了这三位长得相似的类,那么这一次,就来整理一下这三个究竟是什么、为什么出现、怎么用?

Future

是什么

作用:表示异步任务的结果
是否有返回值:有,通过future.get()获取
是否可以new:不可以,通过线程池返回

为什么出现

想象一下,在java多线程下我启动了一个线程/任务,那它执行完之后的结果我怎么拿到?
答:只能用共享变量 + 等待机制,非常麻烦
Future解决了什么问题?(注意是在java多线程下我启动了一个任务后)
任务是否执行完成? 用 isDone() 判断
任务的结果是什么? 用 get() 拿返回值
任务执行是否失败? get() 会抛出异常
想取消任务怎么办? 用 cancel() 方法

怎么用

先举一段使用Future的例子:
你去餐厅点了一个菜(提交任务),然后你去玩手机(做别的事情),等菜好了你再吃(调用 get())。

Future<Integer> future = executorService.submit(() -> {// 点菜return 42;
});
//区间你可以做别的事情
// 吃菜
Integer result = future.get();

这个就是Future最简单的用法,通过将这个点菜任务封装成为一个future,然后丢给线程池,在任务结束后获取任务的结果
再通俗点讲,Future就相当于外卖票据,你可以根据外卖票据来查询外卖完成了吗(future.isDone()),放哪去了(future.get())

Callable

是什么

作用:表示一个可以被线程异步执行,并且有返回值的任务
是否有返回值:有,通过 call() 方法返回
是否可以new:可以自己 new 一个 Callable,也可以用 lambda 表达式写

为什么出现

Runnable 是最早的任务接口,但它有两个缺陷:

  1. 无法返回执行结果
  2. 无法抛出受检异常

而 Callable 接口正好弥补了这两个问题:

  • 它可以有返回值(通过 call 方法)
  • 它可以抛出异常(call 方法支持 throws)

所以当我们希望线程有返回结果或者需要处理异常时,就会使用 Callable,而不是 Runnable

怎么用

先举一段使用 Callable 的例子:
你去餐厅点菜(写好菜单),厨房接单开始做(线程池异步执行),最后服务员把菜端上来(获取返回值)

Callable<Integer> task = () -> {// 做菜return 42;
};
Future<Integer> future = executorService.submit(task);
// 你可以做别的事情
Integer result = future.get(); // 吃菜

这个就是 Callable 最基本的用法,它相当于把一个“带返回值的任务”交给线程池执行,配合 Future 一起使用,就可以实现异步任务的结果处理。

再通俗点讲,Callable 就像是你填写的点菜单,交给服务员去下单,最终 Future 就会根据这个菜单给你送来结果。
在rpc中的应用:
首先我们定义一个函数,参数是Callable callable

/*** 重试** @param callable* @return* @throws Exception*/@Overridepublic RpcResponse doRetry(Callable<RpcResponse> callable) throws Exception {//一些重试策略…………//重试策略return callable.call();}

接着,我们可以将一个任务如发送请求封装进我们的Callable中去

rpcResponse = retryStrategy.doRetry(() ->VertxTcpClient.doRequest(rpcRequest, selectedServiceMetaInfo));

doRetry方法在执行完充实策略后,通过调用callable.call()便可以调用lambda表达式中的函数。
那么这就引出了Callable的另一个用法:将一段逻辑封装成“可延迟执行”的代码块,传递给其他方法,由这个方法在合适的时机调用它。

CompletableFuture

是什么

作用:表示更强大、可组合的异步任务结果,是 Future 的升级版
是否有返回值:有,通过 get()join() 获取
是否可以new:可以,通过 CompletableFuture.supplyAsync() 等方式创建

为什么出现

Future 虽然可以获取异步结果,但有几个明显问题:

  • get() 是阻塞的,不能回调
  • 不能链式调用,比如一个任务完成后接着做另一个任务
  • 多个任务之间组合困难(比如等待多个任务都完成再继续)

CompletableFuture 就是为了解决这些问题而出现的,具备以下能力:
任务是否执行完成? 用 isDone() 判断
任务完成后自动触发下一步? 用 thenApply()thenAccept()
多个任务组合执行? 用 thenCombine()allOf()anyOf()
是否支持异常处理? 支持,用 exceptionally()handle()
是否非阻塞? 支持回调、异步处理,不一定要阻塞调用

怎么用

先举一段使用 CompletableFuture 的例子:
你点了个菜(提交任务),并告诉服务员:菜做好了告诉我(注册回调),你继续刷手机(不阻塞),服务员叫你你再吃菜(消费结果)

CompletableFuture.supplyAsync(() -> {// 点菜return 42;
}).thenAccept(result -> {// 吃菜System.out.println("吃饭结果:" + result);
});

这个就是 CompletableFuture 最简单的用法。它不仅封装了一个异步任务,还支持任务完成后的处理逻辑。

再通俗点讲,CompletableFuture 就像你点外卖时不仅拿到了票据,还附加了功能:
1.外卖送达后自动通知你
2.送多个外卖一起吃
3.出问题时自动换一家重送

外卖送达后自动通知你
你点了一个外卖,外卖准备好后,服务员通过回调通知你去取餐,不需要你手动去查询。

CompletableFuture.supplyAsync(() -> {// 模拟点菜操作return "外卖准备好了";
}).thenAccept(result -> {// 当外卖准备好时自动通知System.out.println("外卖送达: " + result);
});

这里 thenAccept() 就是“外卖准备好后的自动通知”。当异步任务完成后,thenAccept() 会自动执行,通知你去取餐。

送多个外卖一起吃

// 模拟点外卖1
CompletableFuture<String> order1 = CompletableFuture.supplyAsync(() -> {return "外卖1准备好了";
});// 模拟点外卖2
CompletableFuture<String> order2 = CompletableFuture.supplyAsync(() -> {return "外卖2准备好了";
});// 模拟点外卖3
CompletableFuture<String> order3 = CompletableFuture.supplyAsync(() -> {return "外卖3准备好了";
});// 使用 allOf 等待所有外卖都准备好
CompletableFuture<Void> allOrders = CompletableFuture.allOf(order1, order2, order3);// 当所有外卖都准备好后,统一执行后续逻辑
allOrders.thenRun(() -> {try {// 获取所有外卖的结果并打印System.out.println(order1.get());Sy

CompletableFuture.allOf() 会等待 order1、order2 和 order3 三个任务都完成。
一旦所有任务都完成,thenRun() 会被触发,表示你可以去取所有的外卖。
在 thenRun() 中,我们使用 get() 方法获取每个外卖任务的结果,然后打印出来。

出问题时自动换一家重送
如果外卖店发生了问题(比如请求失败),我们可以自动选择另一家餐厅重送。

CompletableFuture<String> order = CompletableFuture.supplyAsync(() -> {// 模拟请求外卖失败throw new RuntimeException("外卖店出现问题");
});order.exceptionally(ex -> {// 发生异常时自动选择另一家餐厅System.out.println("发生异常,换家店: " + ex.getMessage());return "已换一家餐厅";
}).thenAccept(result -> {// 输出最终结果System.out.println("最终外卖结果: " + result);
});

exceptionally() 用来处理异常。如果 order 任务执行时抛出异常,exceptionally() 会捕获到并返回一个默认值(“已换一家餐厅”)。这相当于处理了异常并做了错误补偿,保证程序不会崩溃,最终的外卖结果会通过 thenAccept() 输出。

这下对这三个的理解是不是更深刻了一点,以前老是混淆,也分不清谁是谁,通过外卖的例子,理解了常见的函数操作,以后不要再忘记啦

http://www.xdnf.cn/news/431767.html

相关文章:

  • 2025年项目管理软件革命:AI与国产化浪潮如何重塑企业协作生态
  • tc qdisc参数详解
  • 智慧校园场景下iVX 研发基座应用实践与行业适配研究
  • Milvus(21):过滤搜索、范围搜索、分组搜索
  • python面试实战经验分享
  • Python 实战:如何智能修改字典中的实体值?
  • 从 Vue3 回望 Vue2:响应式的内核革命
  • 集成设备管理(IDM)
  • Android组件权威解析:Activity与Fragment的深度探索与实战
  • 双种群进化算法:动态约束处理与资源分配解决约束多目标优化问题
  • AI模拟了一场5亿年的进化
  • Python Django基于模板的药品名称识别系统【附源码、文档说明】
  • 支付宝小程序开发指南
  • servlet-api
  • 转发多台px4仿真UDP数据到地面站
  • R²AIN SUITE:AI+文档切片,重塑知识管理新标杆
  • Sails.js 知识框架整理
  • 超声波传感器模块
  • 消息~组件(群聊类型)ConcurrentHashMap发送
  • 自适应稀疏核卷积网络:一种高效灵活的图像处理方案
  • Java自定义线程池:从原理到高性能实践
  • NY164NY165美光固态闪存NY166NY172
  • 医疗设备EMC测试为什么推荐GRJ1080B系列滤波器?
  • 工作常用的git命令
  • APS排程系统(Advanced Planning and Scheduling,高级计划与排程系统)
  • U-BOOT
  • talk-centos6之间实现
  • 记忆化回溯搜索-@cache --> 动态规划
  • DevExpressWinForms-布局容器之GroupControl
  • MongoDB+Nginx高可用技术方案