Java 并发编程:Callable、Future 与 CompletableFuture

一、Callable 与 Runnable 对比

特性 Runnable Callable<V>
返回值 无(void) 有(V 类型)
异常处理 不能抛出 checked Exception 可声明抛出 Exception
函数式接口方法 run() call()
适用场景 无需返回结果的异步任务 需要返回结果 / 异常处理的异步任务

二、Future 核心

1. 定义与作用

Future 是对异步任务执行结果的抽象,提供对任务的取消、状态查询、结果获取能力,是 Callable 的 "结果载体"。

2. 核心 API
方法 功能描述 异常说明
boolean cancel(boolean mayInterruptIfRunning) 取消任务,参数指定是否中断运行中任务 -
boolean isCancelled() 判断任务是否在完成前被取消 -
boolean isDone() 判断任务是否完成(正常终止 / 异常 / 取消均返回 true) -
V get() 阻塞获取结果 InterruptedException(线程被中断)、ExecutionException(任务执行异常)、CancellationException(任务被取消)
V get(long timeout, TimeUnit unit) 超时阻塞获取结果 新增 TimeoutException(超时)
3. FutureTask 实现
  • 实现关系:FutureTask implements RunnableFuture extends Runnable, Future

  • 核心作用:作为 "生产者 - 消费者" 桥梁

    • 生产者:执行 Callable 任务,存储结果和状态
    • 消费者:通过 Future 接口获取结果 / 状态
  • 使用流程:

    复制代码
    // 1. 创建Callable任务
    Callable<Integer> task = () -> {
        // 业务逻辑
        return 100;
    };
    // 2. 包装为FutureTask
    FutureTask<Integer> futureTask = new FutureTask<>(task);
    // 3. 提交执行(线程池/新线程)
    new Thread(futureTask).start();
    // 4. 获取结果(阻塞)
    Integer result = futureTask.get();
4. 应用场景:并行查询商品信息
  • 问题:同步查询 5 个接口(各 50ms)需 250ms+
  • 解决方案:用 Future 并行执行,总耗时≈最长接口耗时(50ms)
  • 代码示例:见原文档FutureTaskDemo2
5. 局限性
  1. 结果获取阻塞,无回调机制
  2. 无法链式调用多个任务
  3. 不能组合多个任务结果
  4. 缺乏异常处理机制

三、CompletableFuture 核心(Future 增强版)

1. 核心优势
  • 实现 Future 接口,兼容原有功能
  • 支持任务编排(串行 / 并行 / 聚合)
  • 提供回调机制,非阻塞获取结果
  • 内置异常处理
  • 支持自定义线程池
2. 异步任务创建(4 个静态方法)
方法 特点 返回值
runAsync(Runnable) 无返回值,默认线程池 CompletableFuture<Void>
runAsync(Runnable, Executor) 无返回值,自定义线程池 CompletableFuture<Void>
supplyAsync(Supplier<U>) 有返回值,默认线程池 CompletableFuture
supplyAsync(Supplier<U>, Executor) 有返回值,自定义线程池 CompletableFuture

注意:默认线程池为ForkJoinPool.commonPool(),建议按业务自定义线程池避免线程饥饿

3. 结果获取方式
方法 异常处理 特点
get() checked 异常(需捕获 / 抛出) 阻塞
join() unchecked 异常(无需强制捕获) 阻塞
4. 核心功能分类
(1)结果处理(无返回新结果)
  • whenComplete(BiConsumer<T, Throwable>):处理正常结果 / 异常

  • exceptionally(Function<Throwable, T>):异常时返回默认值

  • 示例:

    复制代码
    CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
        if (new Random().nextInt(10) % 2 == 0) {
            throw new RuntimeException("测试异常");
        }
        return "成功";
    });
    future.whenComplete((result, ex) -> {
        System.out.println("结果:" + result + ",异常:" + ex);
    }).exceptionally(ex -> {
        System.out.println("异常处理:" + ex.getMessage());
        return "默认值";
    });
(2)结果转换(返回新 CompletableFuture)
方法 作用 区别
thenApply(Function<T, U>) 转换结果类型 同一 CompletableFuture 内转换
thenCompose(Function<T, CompletableFuture<U>>) 依赖任务串联 生成新 CompletableFuture,避免嵌套
  • 对比示例:

    复制代码
    // thenApply:转换泛型类型
    CompletableFuture<String> f1 = CompletableFuture.supplyAsync(() -> "Hello")
        .thenApply(s -> s + " World");
    
    // thenCompose:串联依赖任务
    CompletableFuture<String> f2 = CompletableFuture.supplyAsync(() -> "Hello")
        .thenCompose(s -> CompletableFuture.supplyAsync(() -> s + " World"));
(3)结果消费(无返回值)
方法 作用 适用场景
thenAccept(Consumer<T>) 消费单个结果 单任务结果处理
thenAcceptBoth(CompletableFuture<U>, BiConsumer<T, U>) 消费两个任务结果 两个任务都完成后处理
thenRun(Runnable) 不关心结果,执行动作 任务完成后触发后续操作
(4)任务组合
组合类型 方法 作用
And 聚合 thenCombine(CompletableFuture<U>, BiFunction<T, U, V>) 两个任务完成后合并结果
And 聚合 runAfterBoth(CompletableFuture<?>, Runnable) 两个任务完成后执行动作(无返回)
Or 聚合 applyToEither(CompletableFuture<T>, Function<T, U>) 取先完成任务的结果并转换
Or 聚合 acceptEither(CompletableFuture<T>, Consumer<T>) 取先完成任务的结果并消费
Or 聚合 runAfterEither(CompletableFuture<?>, Runnable) 任意一个任务完成后执行动作
多任务 allOf(CompletableFuture<?>... cfs) 等待所有任务完成(无返回值)
多任务 anyOf(CompletableFuture<?>... cfs) 等待任意一个任务完成(返回该任务结果)
5. 经典案例:烧水泡茶(最优工序)
复制代码
// 任务1:洗水壶->烧开水(无返回值)
CompletableFuture<Void> f1 = CompletableFuture.runAsync(() -> {
    System.out.println("T1:洗水壶(1s)");
    sleep(1);
    System.out.println("T1:烧开水(15s)");
    sleep(15);
});

// 任务2:洗茶壶->洗茶杯->拿茶叶(有返回值)
CompletableFuture<String> f2 = CompletableFuture.supplyAsync(() -> {
    System.out.println("T2:洗茶壶(1s)");
    sleep(1);
    System.out.println("T2:洗茶杯(2s)");
    sleep(2);
    System.out.println("T2:拿茶叶(1s)");
    sleep(1);
    return "龙井";
});

// 任务3:两个任务完成后泡茶
CompletableFuture<String> f3 = f1.thenCombine(f2, (__, tea) -> {
    System.out.println("T1:拿到茶叶" + tea + ",开始泡茶");
    return "上茶:" + tea;
});

System.out.println(f3.join()); // 输出:上茶:龙井

四、关键类关系图

五、核心总结

  1. 技术演进Runnable(无返回无异常)→ Callable(有返回有异常)→ Future(结果管理)→ CompletableFuture(任务编排 + 回调)
  2. 核心选择
    • 简单无返回异步任务:Runnable
    • 需返回结果 / 异常处理:Callable+Future
    • 复杂任务依赖(串行 / 并行 / 聚合):CompletableFuture
  3. 使用注意事项
    • 避免使用 CompletableFuture 默认线程池,按业务自定义
    • 优先使用 join () 而非 get () 简化异常处理
    • 复杂任务编排优先使用 CompletableFuture 的回调机制,减少阻塞
相关推荐
码云数智-园园2 小时前
深入理解与正确实现 .NET 中的 BackgroundService
java·开发语言
好好研究2 小时前
SpringBoot整合SpringMVC
xml·java·spring boot·后端·mvc
千寻技术帮2 小时前
10386_基于SpringBoot的外卖点餐管理系统
java·spring boot·vue·外卖点餐
曹轲恒2 小时前
SpringBoot整合SpringMVC(末)
java·spring boot·后端
_周游2 小时前
Java8 API 文档搜索引擎_2.索引模块(程序)
java·搜索引擎·intellij-idea
小马爱打代码2 小时前
Spring Boot:邮件发送生产可落地方案
java·spring boot·后端
BD_Marathon2 小时前
设计模式——接口隔离原则
java·设计模式·接口隔离原则
空空kkk2 小时前
SSM项目练习——hami音乐(二)
java
强风7942 小时前
Linux—应用层自定义协议与序列化
运维·服务器·网络