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 的回调机制,减少阻塞
相关推荐
稻草猫.9 小时前
SpringBoot日志全解析:从调试到持久化
java·开发语言·spring boot·java-ee·idea
zopple9 小时前
Knife4j文档请求异常(基于SpringBoot3,查找原因并解决)
java·服务器·数据库
rgb2gray9 小时前
论文详解 | TWScan:基于收紧窗口的增强扫描统计,实现不规则形状空间热点精准检测
网络·人工智能·python·pandas·交通安全·出租车
清水白石0089 小时前
Python 弱引用深度解析——让缓存不再成为内存泄漏的温床
java·python·缓存
zzb15809 小时前
RAG from Scratch-优化-routing
java·前端·网络·人工智能·后端·python·mybatis
流水迢迢lst9 小时前
靶场练习day14--任意文件读取
网络·安全
深蓝轨迹10 小时前
IDEA 中 Spring Boot 配置文件的自动提示消失(无法扫描配置文件)的完整解决方案
java·spring boot·intellij-idea
杀神lwz10 小时前
Java Json压缩工具类
java·json
虾..10 小时前
Linux 基于TCP实现服务端客户端通信(线程池)
java·网络协议·tcp/ip
前端小雪的博客.10 小时前
【Java 基础】变量全解:定义、命名规范、作用域与常量(附代码示例+面试题)
java·作用域·java基础·java入门·变量·常量·java面试题