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

并发 - Callable 与 Future

知识点 5.1:并发编程进阶 —— Callable 与 Future

在学习了 Runnable 之后,我们很快会发现它的两个主要局限:

  1. run() 方法没有返回值。
  2. run() 方法不能抛出受检异常。

为了解决这两个问题,JUC 提供了一对更强大的组合:CallableFuture

1. 核心理论:Callable 接口

java.util.concurrent.Callable 是一个类似于 Runnable 的接口,但功能更强。

@FunctionalInterface
public interface Callable<V> {V call() throws Exception;
}

从源码可以看出它与 Runnable 的三大区别:

  1. 方法名不同: 一个是 call(),一个是 run()
  2. 有返回值: call() 方法可以返回一个泛型 V 的结果。
  3. 能抛出异常: call() 方法的签名上声明了 throws Exception,这意味着你可以在任务中抛出受检异常。

2. 深度剖析:Future —— 未来的“提货单”

既然 Callable 能返回结果,那我们怎么获取这个结果呢?

直接用一个变量去接 call() 的返回值是行不通的,因为 call() 是在另一个线程中异步执行的。主线程提交任务后,不会傻傻地一直等着,它会继续做自己的事。

为了解决这个问题,JUC 设计了 Future 接口。当你把一个 Callable 任务提交给线程池时,线程池会立刻返回一个 Future 对象。这个 Future 对象就像一张“提货单”“承诺书”

Future 接口的核心方法:

  • V get(): 这是一个阻塞方法。当主线程调用它时:

    • 如果任务已经执行完毕,它会立刻返回 Callable 的执行结果。
    • 如果任务还在执行中,主线程会阻塞在这里,一直等到任务执行完毕再返回结果。
    • 如果任务在执行过程中抛出了异常,那么 get() 方法会把那个异常原封不动地再次抛出来。
  • V get(long timeout, TimeUnit unit): 带超时的 get()。主线程最多只阻塞指定的时间。如果超时后任务还没完成,会抛出 TimeoutException

  • boolean isDone(): 判断任务是否已经执行完成(无论是正常完成、异常终止还是被取消)。这个方法是非阻塞的,常用于轮询。

  • boolean cancel(boolean mayInterruptIfRunning): 尝试取消任务的执行。


3. 生活中的例子与代码示例

  • 生活比喻: 你去一家高级咖啡店点了一杯手冲咖啡(一个耗时的 Callable 任务)。

    • 你下单后,店员不会让你站在原地干等,而是会给你一个取餐牌Future 对象),然后你就可以回到座位上玩手机了(主线程继续执行)。
    • 你随时可以看一眼取餐牌上的状态灯(调用 isDone()),看看咖啡好了没。
    • 当你觉得差不多了,就拿着取餐牌去柜台取咖啡(调用 get())。如果咖啡师正在做,你就得在柜台前等一会儿(get() 阻塞)。如果咖啡做好了,你就能拿到咖啡(获取返回值)。如果制作过程中咖啡豆用完了(任务抛出异常),店员会告诉你这个坏消息(get() 抛出异常)。
  • 核心代码示例:

package com.study.concurrency;import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;public class CallableAndFutureExample {public static void main(String[] args) {ExecutorService executor = Executors.newFixedThreadPool(2);// 传统方式:创建一个实现了 Callable 接口的类Callable<String> traditionalTask = new Callable<String>() {@Overridepublic String call() throws Exception {Thread.sleep(1000);return "传统方式执行完毕";}};Future<String> future1 = executor.submit(traditionalTask);// Lambda 方式 (Java 8+ 推荐)// 因为 Callable 也是函数式接口,所以可以直接用 Lambda 表达式Callable<String> lambdaTask = () -> {Thread.sleep(2000);// 模拟抛出异常if (true) { // a conditionthrow new IllegalStateException("Lambda 任务出错了");}return "Lambda 方式执行完毕";};Future<String> future2 = executor.submit(lambdaTask);System.out.println("主线程:已提交2个任务。");try {System.out.println("获取传统任务结果: " + future1.get());} catch (InterruptedException | ExecutionException e) {e.printStackTrace();}try {System.out.println("获取 Lambda 任务结果: " + future2.get());} catch (InterruptedException | ExecutionException e) {// 注意:原始异常被包装在 ExecutionException 中System.err.println("获取 Lambda 任务结果时出错: " + e.getCause());}executor.shutdown();}
}
http://www.jsqmd.com/news/279380/

相关文章:

  • 麦橘超然性能压测报告:单次生成耗时统计
  • 2026营口市英语雅思培训辅导机构推荐;2026权威出国雅思课程排行榜
  • fft npainting lama高阶使用技巧:分层修复与边缘羽化实战案例
  • 企业级通信如何选型?(MCP与OpenAI Function Calling技术对决揭秘)
  • OOP 经典对比
  • YOLOv11+BiFPN革新小麦杂质检测技术
  • 手把手教你实现MCP服务器resources热更新,动态调整不再重启服务
  • 山石网科各硬件产品Console配置口波特率汇总
  • 揭秘Dify Iteration节点:如何高效处理复杂列表数据?
  • Java集合类框架的基本接口有哪些?
  • 为什么FSMN VAD总检测失败?参数调优实战教程入门必看
  • 基于51单片机智能手环老人防跌倒报警器GSM短信上报设计套件106(设计源文件+万字报告+讲解)(支持资料、图片参考_相关定制)_文章底部可以扫码
  • 强化学习十年演进
  • Live Avatar降本部署方案:单GPU+CPU offload低配环境实操教程
  • RTX5060显卡对PyTorch与CUDA适配问题解决方案(解决环境依赖问题AI微调部署前奏)
  • 2026锦州市英语雅思培训辅导机构推荐;2026权威出国雅思课程排行榜
  • 紧急警告:错误配置导致Claude Desktop丢失MCP Server连接(附修复方案)
  • 广东激光熔敷公司怎么选,哪家口碑好?
  • GEO优化公司推荐哪家好?从技术深度到服务能力的权威解析!
  • Pinterest注册失败怎么办?2026最新解决指南在这里
  • Unsloth资源占用监控:GPU显存与CPU使用率跟踪方法
  • Paraformer-large语音识别合规性:金融行业落地实践
  • 盘点人工智能转型服务方案,广东省哪家口碑好费用低
  • 【Dify部署避坑指南】:解决上传文件413错误的5种高效方案
  • 分析成都太阳能板定制厂家,员工素质哪家高
  • 2026 AEO认证咨询推荐:专业服务助力企业通关效率提升
  • 【Web安全】什么是XSS攻击?如何实现手动XSS,利用BeEF执行XSS攻击?
  • 写论文找不到外国文献?方法合集来了!实用检索技巧助你高效获取外文文献资源
  • Java 开发中的良好的小习惯
  • 最新成行业标准的CAIE证书,报考前必看的坑