Java Future 与 CompletableFuture 实战指南:从异步结果到任务编排

简介

FutureCompletableFuture 都属于 Java 并发编程里的异步结果模型。

简单理解:

text 复制代码
Future
  |
  v
异步任务的结果占位符
text 复制代码
CompletableFuture
  |
  v
可以链式处理、组合编排、异常兜底的异步结果

Future 从 Java 5 开始出现,主要解决一个问题:

text 复制代码
任务提交到线程池后,后面还能拿到任务结果。

CompletableFuture 从 Java 8 开始出现,解决的问题更进一步:

text 复制代码
异步任务完成后,可以继续转换、消费、组合、兜底,不必只靠 get() 阻塞等待。

一句话概括:

text 复制代码
Future 适合接收一个异步任务的结果,CompletableFuture 更适合编排一组异步任务。

Future 解决什么问题

普通 Thread 执行任务时,结果不好直接返回。

java 复制代码
Thread thread = new Thread(() -> {
    int result = 100 + 200;
});

thread.start();

这里的 result 只能留在线程内部。

如果希望任务在后台执行,同时主流程后面还能拿到结果,就可以使用 ExecutorServiceFuture

执行流程:

text 复制代码
提交 Callable 任务
  |
  v
线程池异步执行
  |
  v
立即返回 Future
  |
  v
需要结果时调用 get()

Future 第一个 Demo

java 复制代码
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

public class FutureFirstDemo {

    public static void main(String[] args) throws Exception {
        ExecutorService executor = Executors.newFixedThreadPool(2);

        Callable<Integer> task = () -> {
            Thread.sleep(1000);
            return 100 + 200;
        };

        Future<Integer> future = executor.submit(task);

        System.out.println("任务已提交");
        System.out.println("主流程继续执行");

        Integer result = future.get();
        System.out.println("异步结果:" + result);

        executor.shutdown();
    }
}

输出类似:

text 复制代码
任务已提交
主流程继续执行
异步结果:300

submit 会立即返回一个 Future

future.get() 会阻塞当前线程,直到任务完成。

Future 常用方法

方法 作用
get() 阻塞等待任务完成并返回结果
get(timeout, unit) 最多等待指定时间
isDone() 判断任务是否完成
cancel(mayInterruptIfRunning) 尝试取消任务
isCancelled() 判断任务是否已取消

Future 超时和取消

get() 如果一直等不到结果,当前线程会一直卡住。

更稳妥的写法是带超时:

java 复制代码
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

public class FutureTimeoutDemo {

    public static void main(String[] args) throws Exception {
        ExecutorService executor = Executors.newFixedThreadPool(1);

        Future<String> future = executor.submit(() -> {
            Thread.sleep(3000);
            return "远程接口结果";
        });

        try {
            String result = future.get(1, TimeUnit.SECONDS);
            System.out.println(result);
        } catch (TimeoutException e) {
            future.cancel(true);
            System.out.println("任务超时,已尝试取消");
        } finally {
            executor.shutdown();
        }
    }
}

cancel(true) 会尝试中断正在执行的任务。

任务内部如果捕获了 InterruptedException,需要恢复中断状态:

java 复制代码
try {
    Thread.sleep(3000);
} catch (InterruptedException e) {
    Thread.currentThread().interrupt();
    return "任务被取消";
}

Future 的局限

Future 能拿到异步任务结果,但表达能力比较基础。

常见痛点:

  • get() 会阻塞当前线程
  • 任务完成后不能直接注册回调
  • 多个任务组合时需要手写等待和取值
  • 异常处理集中在 get() 周围
  • 不能方便地把一个异步结果继续接到另一个异步任务

比如下面这种接口聚合:

text 复制代码
查用户
查订单
查账户
合并结果

Future 能写,但代码会围绕多个 get() 展开。

任务越多,流程越散。

CompletableFuture 是什么

CompletableFuture<T> 同时实现了:

text 复制代码
Future<T>
CompletionStage<T>

它既能像 Future 一样表示异步结果,也能像流水线一样继续编排后续动作。

它支持:

  • 创建异步任务
  • 转换结果
  • 消费结果
  • 串行异步调用
  • 并行合并结果
  • 等待多个任务完成
  • 任意一个任务完成就继续
  • 异常兜底
  • 超时控制
  • 手动完成结果

第一个 CompletableFuture Demo

java 复制代码
import java.util.concurrent.CompletableFuture;

public class CompletableFutureFirstDemo {

    public static void main(String[] args) {
        CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
            sleep(1000);
            return "Java";
        });

        String result = future
                .thenApply(String::toUpperCase)
                .thenApply(value -> "Hello " + value)
                .join();

        System.out.println(result);
    }

    private static void sleep(long millis) {
        try {
            Thread.sleep(millis);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            throw new RuntimeException(e);
        }
    }
}

输出:

text 复制代码
Hello JAVA

这里没有在中间步骤里手动 get()

任务完成后会自动进入 thenApply

runAsync 和 supplyAsync

CompletableFuture 创建异步任务常用两个静态方法。

runAsync

没有返回值。

java 复制代码
CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
    System.out.println("刷新缓存");
});

适合:

  • 发送通知
  • 写日志
  • 刷缓存
  • 执行不需要返回结果的后台动作

supplyAsync

有返回值。

java 复制代码
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
    return "商品详情";
});

适合:

  • 查询远程接口
  • 查询数据库
  • 计算一个结果
  • 读取文件内容

默认线程池

没有指定 Executor 时,runAsyncsupplyAsync 默认使用 ForkJoinPool.commonPool()

java 复制代码
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
    return Thread.currentThread().getName();
});

输出类似:

text 复制代码
ForkJoinPool.commonPool-worker-1

默认池适合简单 Demo。

业务项目里更常见的是传入自定义线程池:

java 复制代码
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
    return "订单数据";
}, orderExecutor);

原因很简单:

text 复制代码
不同业务任务隔离
可以控制队列长度
可以设置线程名称
可以观察拒绝策略
可以避免所有异步任务挤在 commonPool

自定义线程池

java 复制代码
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

public class AsyncExecutors {

    public static ExecutorService orderExecutor() {
        AtomicInteger counter = new AtomicInteger(1);

        ThreadFactory threadFactory = runnable -> {
            Thread thread = new Thread(runnable);
            thread.setName("order-async-" + counter.getAndIncrement());
            return thread;
        };

        return new ThreadPoolExecutor(
                8,
                16,
                60,
                TimeUnit.SECONDS,
                new ArrayBlockingQueue<>(500),
                threadFactory,
                new ThreadPoolExecutor.CallerRunsPolicy()
        );
    }
}

使用:

java 复制代码
ExecutorService executor = AsyncExecutors.orderExecutor();

CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
    return "订单查询结果";
}, executor);

System.out.println(future.join());

executor.shutdown();

线程池参数需要结合业务压测调整。

如果任务主要是 I/O 等待,也可以结合虚拟线程:

java 复制代码
ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor();

CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
    return callRemoteService();
}, executor);

get 和 join

Future 常用 get()

CompletableFuture 也有 get(),但更多代码会使用 join()

方法 异常形式 说明
get() InterruptedExceptionExecutionException 受检异常,需要显式处理
get(timeout, unit) 额外抛 TimeoutException 带等待时间
join() CompletionException 非受检异常,链式代码里更常见
getNow(defaultValue) 不阻塞 未完成时返回默认值

示例:

java 复制代码
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> "OK");

String result = future.join();
System.out.println(result);

如果任务异常,join() 会抛出 CompletionException

java 复制代码
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
    throw new IllegalStateException("库存服务异常");
});

try {
    future.join();
} catch (CompletionException e) {
    System.out.println(e.getCause().getMessage());
}

thenApply:转换结果

thenApply 用来把上一步结果转换成另一个值。

java 复制代码
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> "java")
        .thenApply(String::toUpperCase)
        .thenApply(value -> "语言:" + value);

System.out.println(future.join());

输出:

text 复制代码
语言:JAVA

类比同步写法:

java 复制代码
String value = "java";
String upper = value.toUpperCase();
String result = "语言:" + upper;

thenApply 适合纯内存转换,不适合在里面继续发起新的异步任务。

thenAccept:消费结果

thenAccept 接收上一步结果,但不返回新值。

java 复制代码
CompletableFuture<Void> future = CompletableFuture.supplyAsync(() -> "订单创建成功")
        .thenAccept(message -> {
            System.out.println("发送通知:" + message);
        });

future.join();

适合:

  • 打印结果
  • 发送通知
  • 写入日志
  • 推送消息

thenRun:只关心完成信号

thenRun 不接收上一步结果,也不返回新值。

java 复制代码
CompletableFuture<Void> future = CompletableFuture.supplyAsync(() -> "任务结果")
        .thenRun(() -> {
            System.out.println("任务已经完成");
        });

future.join();

适合只关心"完成了"这件事的场景。

thenCompose:串行异步任务

如果第二个任务依赖第一个任务的结果,并且第二个任务本身也是异步的,使用 thenCompose

示例场景:

text 复制代码
先查用户
再根据用户 ID 查订单
java 复制代码
CompletableFuture<User> userFuture = findUser(1001L);

CompletableFuture<Order> orderFuture = userFuture
        .thenCompose(user -> findLatestOrder(user.id()));

完整 Demo:

java 复制代码
import java.util.concurrent.CompletableFuture;

public class ThenComposeDemo {

    public static void main(String[] args) {
        CompletableFuture<Order> future = findUser(1001L)
                .thenCompose(user -> findLatestOrder(user.id()));

        System.out.println(future.join());
    }

    private static CompletableFuture<User> findUser(Long userId) {
        return CompletableFuture.supplyAsync(() -> {
            sleep(300);
            return new User(userId, "张三");
        });
    }

    private static CompletableFuture<Order> findLatestOrder(Long userId) {
        return CompletableFuture.supplyAsync(() -> {
            sleep(500);
            return new Order(9001L, userId, "PAID");
        });
    }

    private static void sleep(long millis) {
        try {
            Thread.sleep(millis);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            throw new RuntimeException(e);
        }
    }

    record User(Long id, String username) {
    }

    record Order(Long id, Long userId, String status) {
    }
}

thenCompose 可以避免这种嵌套:

java 复制代码
CompletableFuture<CompletableFuture<Order>> nested = findUser(1001L)
        .thenApply(user -> findLatestOrder(user.id()));

thenCombine:合并两个独立任务

如果两个任务互不依赖,可以并行发起,再合并结果。

示例场景:

text 复制代码
查用户信息
查账户余额
合并成用户首页
java 复制代码
CompletableFuture<User> userFuture = findUser(1001L);
CompletableFuture<Account> accountFuture = findAccount(1001L);

CompletableFuture<UserHome> homeFuture = userFuture.thenCombine(
        accountFuture,
        (user, account) -> new UserHome(user, account)
);

完整 Demo:

java 复制代码
import java.math.BigDecimal;
import java.util.concurrent.CompletableFuture;

public class ThenCombineDemo {

    public static void main(String[] args) {
        CompletableFuture<UserHome> future = findUser(1001L)
                .thenCombine(findAccount(1001L), UserHome::new);

        System.out.println(future.join());
    }

    private static CompletableFuture<User> findUser(Long userId) {
        return CompletableFuture.supplyAsync(() -> {
            sleep(500);
            return new User(userId, "张三");
        });
    }

    private static CompletableFuture<Account> findAccount(Long userId) {
        return CompletableFuture.supplyAsync(() -> {
            sleep(500);
            return new Account(userId, new BigDecimal("99.80"));
        });
    }

    private static void sleep(long millis) {
        try {
            Thread.sleep(millis);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            throw new RuntimeException(e);
        }
    }

    record User(Long id, String username) {
    }

    record Account(Long userId, BigDecimal balance) {
    }

    record UserHome(User user, Account account) {
    }
}

两个任务都完成后,才会执行合并函数。

allOf:等待全部任务完成

allOf 适合等待一组任务全部完成。

java 复制代码
import java.util.List;
import java.util.concurrent.CompletableFuture;

public class AllOfDemo {

    public static void main(String[] args) {
        List<Long> productIds = List.of(1L, 2L, 3L);

        List<CompletableFuture<Product>> futures = productIds.stream()
                .map(AllOfDemo::findProduct)
                .toList();

        CompletableFuture<Void> allFuture = CompletableFuture.allOf(
                futures.toArray(new CompletableFuture[0])
        );

        List<Product> products = allFuture
                .thenApply(ignore -> futures.stream()
                        .map(CompletableFuture::join)
                        .toList())
                .join();

        System.out.println(products);
    }

    private static CompletableFuture<Product> findProduct(Long productId) {
        return CompletableFuture.supplyAsync(() -> {
            sleep(300);
            return new Product(productId, "商品-" + productId);
        });
    }

    private static void sleep(long millis) {
        try {
            Thread.sleep(millis);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            throw new RuntimeException(e);
        }
    }

    record Product(Long id, String name) {
    }
}

allOf 返回的是 CompletableFuture<Void>

所以需要在全部完成后,从原来的 futures 里逐个 join() 取结果。

anyOf:等待任意一个任务完成

anyOf 适合"谁先返回用谁"的场景。

比如同一份配置可以从多个中心读取:

java 复制代码
import java.util.concurrent.CompletableFuture;

public class AnyOfDemo {

    public static void main(String[] args) {
        CompletableFuture<String> nacosFuture = loadFromNacos();
        CompletableFuture<String> apolloFuture = loadFromApollo();

        CompletableFuture<Object> fastest = CompletableFuture.anyOf(
                nacosFuture,
                apolloFuture
        );

        System.out.println(fastest.join());
    }

    private static CompletableFuture<String> loadFromNacos() {
        return CompletableFuture.supplyAsync(() -> {
            sleep(500);
            return "nacos-config";
        });
    }

    private static CompletableFuture<String> loadFromApollo() {
        return CompletableFuture.supplyAsync(() -> {
            sleep(300);
            return "apollo-config";
        });
    }

    private static void sleep(long millis) {
        try {
            Thread.sleep(millis);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            throw new RuntimeException(e);
        }
    }
}

返回类型是 CompletableFuture<Object>

使用时通常需要做类型转换,或者把结果统一包装成相同类型。

applyToEither:两个任务谁快用谁

applyToEitheranyOf 类似,但它可以继续转换结果,并保留泛型。

java 复制代码
CompletableFuture<String> primary = CompletableFuture.supplyAsync(() -> {
    sleep(500);
    return "primary";
});

CompletableFuture<String> backup = CompletableFuture.supplyAsync(() -> {
    sleep(300);
    return "backup";
});

CompletableFuture<String> result = primary.applyToEither(
        backup,
        value -> "使用结果:" + value
);

System.out.println(result.join());

适合主备接口、同城双活读取等场景。

exceptionally:异常恢复

exceptionally 只在上游异常时执行。

java 复制代码
import java.util.concurrent.CompletableFuture;

public class ExceptionallyDemo {

    public static void main(String[] args) {
        CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
            throw new IllegalStateException("库存服务不可用");
        }).exceptionally(ex -> {
            return "库存状态未知";
        });

        System.out.println(future.join());
    }
}

输出:

text 复制代码
库存状态未知

适合返回兜底值。

handle:正常和异常都处理

handle 不管上游成功还是失败,都会执行。

java 复制代码
import java.util.concurrent.CompletableFuture;

public class HandleDemo {

    public static void main(String[] args) {
        CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
            if (System.currentTimeMillis() > 0) {
                throw new IllegalStateException("支付服务异常");
            }
            return "支付成功";
        }).handle((result, ex) -> {
            if (ex != null) {
                return "支付状态待确认";
            }
            return result;
        });

        System.out.println(future.join());
    }
}

适合把成功和失败统一转换成同一种返回结构。

whenComplete:完成后观察结果

whenComplete 可以拿到结果或异常,但通常不改变最终结果。

java 复制代码
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> "OK")
        .whenComplete((result, ex) -> {
            if (ex != null) {
                System.out.println("记录异常:" + ex.getMessage());
            } else {
                System.out.println("记录结果:" + result);
            }
        });

System.out.println(future.join());

适合:

  • 记录日志
  • 打点统计
  • 释放资源
  • 观察任务结果

如果 whenComplete 里继续抛异常,后续链路也会受到影响。

exceptionally、handle、whenComplete 对比

方法 是否只处理异常 是否能改变结果 常见用途
exceptionally 可以 异常兜底
handle 可以 成功和失败统一转换
whenComplete 通常不改 日志、监控、资源释放

超时控制

Java 9 开始,CompletableFuture 提供了更方便的超时方法。

orTimeout

超时后以异常完成。

java 复制代码
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;

public class OrTimeoutDemo {

    public static void main(String[] args) {
        CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
            sleep(3000);
            return "远程接口结果";
        }).orTimeout(1, TimeUnit.SECONDS)
          .exceptionally(ex -> "接口超时");

        System.out.println(future.join());
    }

    private static void sleep(long millis) {
        try {
            Thread.sleep(millis);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            throw new RuntimeException(e);
        }
    }
}

输出:

text 复制代码
接口超时

completeOnTimeout

超时后用默认值完成。

java 复制代码
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
    sleep(3000);
    return "慢接口结果";
}).completeOnTimeout("默认结果", 1, TimeUnit.SECONDS);

System.out.println(future.join());

completeOnTimeout 适合可以接受默认值的场景。

orTimeout 适合需要显式进入异常处理的场景。

手动完成 CompletableFuture

CompletableFuture 可以先创建出来,后面再由其他线程填入结果。

java 复制代码
import java.util.concurrent.CompletableFuture;

public class ManualCompleteDemo {

    public static void main(String[] args) {
        CompletableFuture<String> future = new CompletableFuture<>();

        Thread thread = new Thread(() -> {
            try {
                Thread.sleep(500);
                future.complete("回调结果");
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                future.completeExceptionally(e);
            }
        });

        thread.start();

        System.out.println(future.join());
    }
}

更常见的写法是把 completeExceptionally 放在回调桥接代码里:

java 复制代码
CompletableFuture<String> future = new CompletableFuture<>();

legacyClient.callAsync(new LegacyCallback() {
    @Override
    public void onSuccess(String result) {
        future.complete(result);
    }

    @Override
    public void onError(Throwable throwable) {
        future.completeExceptionally(throwable);
    }
});

这种写法适合把老式回调 API 包装成 CompletableFuture

实战:用户首页聚合接口

场景:

text 复制代码
用户首页需要同时展示:
用户信息
账户余额
最近订单
优惠券数量

这些数据来自不同服务,互相之间没有依赖关系,适合并发查询。

领域模型:

java 复制代码
import java.math.BigDecimal;
import java.util.List;

public record UserInfo(Long userId, String username) {
}

public record AccountInfo(Long userId, BigDecimal balance) {
}

public record OrderInfo(Long orderId, String title) {
}

public record CouponInfo(Integer count) {
}

public record UserHomeResponse(
        UserInfo user,
        AccountInfo account,
        List<OrderInfo> orders,
        CouponInfo coupon
) {
}

远程服务模拟:

java 复制代码
import java.math.BigDecimal;
import java.util.List;

public class RemoteClients {

    public UserInfo findUser(Long userId) {
        sleep(300);
        return new UserInfo(userId, "张三");
    }

    public AccountInfo findAccount(Long userId) {
        sleep(500);
        return new AccountInfo(userId, new BigDecimal("88.60"));
    }

    public List<OrderInfo> findRecentOrders(Long userId) {
        sleep(700);
        return List.of(
                new OrderInfo(9001L, "Java 并发编程"),
                new OrderInfo(9002L, "Spring Boot 实战")
        );
    }

    public CouponInfo findCoupon(Long userId) {
        sleep(200);
        return new CouponInfo(3);
    }

    private void sleep(long millis) {
        try {
            Thread.sleep(millis);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            throw new RuntimeException(e);
        }
    }
}

聚合服务:

java 复制代码
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

public class UserHomeService {

    private final RemoteClients remoteClients = new RemoteClients();
    private final ExecutorService executor = Executors.newFixedThreadPool(8);

    public UserHomeResponse loadHome(Long userId) {
        CompletableFuture<UserInfo> userFuture = CompletableFuture.supplyAsync(
                () -> remoteClients.findUser(userId),
                executor
        );

        CompletableFuture<AccountInfo> accountFuture = CompletableFuture.supplyAsync(
                () -> remoteClients.findAccount(userId),
                executor
        );

        CompletableFuture<List<OrderInfo>> orderFuture = CompletableFuture.supplyAsync(
                () -> remoteClients.findRecentOrders(userId),
                executor
        );

        CompletableFuture<CouponInfo> couponFuture = CompletableFuture.supplyAsync(
                () -> remoteClients.findCoupon(userId),
                executor
        ).completeOnTimeout(new CouponInfo(0), 300, TimeUnit.MILLISECONDS);

        return CompletableFuture
                .allOf(userFuture, accountFuture, orderFuture, couponFuture)
                .thenApply(ignore -> new UserHomeResponse(
                        userFuture.join(),
                        accountFuture.join(),
                        orderFuture.join(),
                        couponFuture.join()
                ))
                .orTimeout(2, TimeUnit.SECONDS)
                .join();
    }

    public void shutdown() {
        executor.shutdown();
    }
}

测试入口:

java 复制代码
public class UserHomeDemo {

    public static void main(String[] args) {
        UserHomeService service = new UserHomeService();

        try {
            long start = System.currentTimeMillis();
            UserHomeResponse response = service.loadHome(1001L);
            long cost = System.currentTimeMillis() - start;

            System.out.println(response);
            System.out.println("耗时:" + cost + " ms");
        } finally {
            service.shutdown();
        }
    }
}

这里四个查询并发执行,总耗时主要取决于最慢的那个任务,而不是四个任务耗时相加。

优惠券接口使用了 completeOnTimeout

如果优惠券服务慢了,首页仍然可以返回,只是优惠券数量按 0 处理。

实战:下单流程串并结合

并不是所有任务都能并发。

下单流程通常既有依赖关系,也有独立查询。

示例流程:

text 复制代码
校验订单
  |
  v
并发查询用户、库存、价格
  |
  v
扣减库存
  |
  v
创建订单

代码示例:

java 复制代码
import java.math.BigDecimal;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class OrderCreateService {

    private final ExecutorService executor = Executors.newFixedThreadPool(8);

    public CompletableFuture<OrderCreateResult> create(OrderCreateCommand command) {
        return validate(command)
                .thenCompose(validCommand -> {
                    CompletableFuture<UserInfo> userFuture = findUser(validCommand.userId());
                    CompletableFuture<StockInfo> stockFuture = checkStock(validCommand.productId());
                    CompletableFuture<PriceInfo> priceFuture = findPrice(validCommand.productId());

                    return CompletableFuture
                            .allOf(userFuture, stockFuture, priceFuture)
                            .thenCompose(ignore -> deductStock(stockFuture.join()))
                            .thenApply(stockResult -> new OrderCreateResult(
                                    validCommand.userId(),
                                    validCommand.productId(),
                                    priceFuture.join().price(),
                                    stockResult.success()
                            ));
                });
    }

    private CompletableFuture<OrderCreateCommand> validate(OrderCreateCommand command) {
        return CompletableFuture.supplyAsync(() -> {
            if (command.count() <= 0) {
                throw new IllegalArgumentException("商品数量需要大于 0");
            }
            return command;
        }, executor);
    }

    private CompletableFuture<UserInfo> findUser(Long userId) {
        return CompletableFuture.supplyAsync(() -> new UserInfo(userId, "张三"), executor);
    }

    private CompletableFuture<StockInfo> checkStock(Long productId) {
        return CompletableFuture.supplyAsync(() -> new StockInfo(productId, 100), executor);
    }

    private CompletableFuture<PriceInfo> findPrice(Long productId) {
        return CompletableFuture.supplyAsync(() -> new PriceInfo(productId, new BigDecimal("59.90")), executor);
    }

    private CompletableFuture<StockResult> deductStock(StockInfo stockInfo) {
        return CompletableFuture.supplyAsync(() -> {
            if (stockInfo.available() <= 0) {
                throw new IllegalStateException("库存不足");
            }
            return new StockResult(true);
        }, executor);
    }

    public void shutdown() {
        executor.shutdown();
    }

    public record OrderCreateCommand(Long userId, Long productId, Integer count) {
    }

    public record StockInfo(Long productId, Integer available) {
    }

    public record PriceInfo(Long productId, BigDecimal price) {
    }

    public record StockResult(Boolean success) {
    }

    public record OrderCreateResult(
            Long userId,
            Long productId,
            BigDecimal price,
            Boolean stockDeducted
    ) {
    }
}

这段代码里:

text 复制代码
validate -> 串行前置
findUser / checkStock / findPrice -> 并行
deductStock -> 依赖库存检查结果
thenApply -> 组装最终结果

Spring Boot 中返回 CompletableFuture

Spring MVC Controller 可以直接返回 CompletableFuture<T>

Maven 依赖:

xml 复制代码
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

异步线程池:

java 复制代码
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

import java.util.concurrent.Executor;

@Configuration
@EnableAsync
public class AsyncConfig {

    @Bean("orderAsyncExecutor")
    public Executor orderAsyncExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(8);
        executor.setMaxPoolSize(16);
        executor.setQueueCapacity(500);
        executor.setThreadNamePrefix("order-async-");
        executor.initialize();
        return executor;
    }
}

Service:

java 复制代码
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

import java.util.concurrent.CompletableFuture;

@Service
public class SpringOrderService {

    @Async("orderAsyncExecutor")
    public CompletableFuture<String> createOrder(Long userId, Long productId) {
        try {
            Thread.sleep(500);
            return CompletableFuture.completedFuture(
                    "订单创建成功,userId=" + userId + ", productId=" + productId
            );
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            return CompletableFuture.failedFuture(e);
        }
    }
}

Controller:

java 复制代码
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import java.util.concurrent.CompletableFuture;

@RestController
@RequestMapping("/api/orders")
public class SpringOrderController {

    private final SpringOrderService orderService;

    public SpringOrderController(SpringOrderService orderService) {
        this.orderService = orderService;
    }

    @PostMapping
    public CompletableFuture<String> create(
            @RequestParam Long userId,
            @RequestParam Long productId
    ) {
        return orderService.createOrder(userId, productId);
    }
}

访问:

text 复制代码
POST http://localhost:8080/api/orders?userId=1001&productId=2001

CompletableFuture 和虚拟线程

Java 21 以后,I/O 密集型异步任务可以考虑使用虚拟线程执行器。

java 复制代码
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class CompletableFutureVirtualThreadDemo {

    public static void main(String[] args) {
        try (ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor()) {
            CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
                sleep(500);
                return "虚拟线程执行结果:" + Thread.currentThread();
            }, executor);

            System.out.println(future.join());
        }
    }

    private static void sleep(long millis) {
        try {
            Thread.sleep(millis);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            throw new RuntimeException(e);
        }
    }
}

需要注意的是,CompletableFuture 负责任务编排,虚拟线程负责承载阻塞任务。

二者解决的问题不同:

text 复制代码
CompletableFuture:表达异步依赖关系
虚拟线程:降低阻塞任务的线程成本

普通同步代码如果只是想并发跑多个阻塞任务,也可以直接使用虚拟线程执行器和 Future

如果需要复杂编排、异常兜底、任务组合,CompletableFuture 仍然很合适。

常见使用建议

为异步任务指定 Executor

业务代码里建议显式传入线程池。

java 复制代码
CompletableFuture.supplyAsync(() -> loadData(), executor);

这样可以避免多个业务共用默认 ForkJoinPool.commonPool() 后互相影响。

在线程池里设置有界队列

异步任务不是越多越好。

有界队列和拒绝策略可以让系统在压力过大时更早暴露问题。

java 复制代码
new ThreadPoolExecutor(
        8,
        16,
        60,
        TimeUnit.SECONDS,
        new ArrayBlockingQueue<>(500),
        threadFactory,
        new ThreadPoolExecutor.CallerRunsPolicy()
);

区分 thenApply 和 thenCompose

返回普通值时用 thenApply

java 复制代码
CompletableFuture<String> nameFuture = userFuture
        .thenApply(UserInfo::username);

返回 CompletableFuture 时用 thenCompose

java 复制代码
CompletableFuture<List<OrderInfo>> orderFuture = userFuture
        .thenCompose(user -> findRecentOrders(user.userId()));

join 放在边界处

join() 会阻塞当前线程。

更清晰的写法是:

text 复制代码
中间流程继续返回 CompletableFuture
最外层边界再 join

例如:

java 复制代码
public CompletableFuture<UserHomeResponse> loadHomeAsync(Long userId) {
    return buildHomeFuture(userId);
}

public UserHomeResponse loadHome(Long userId) {
    return loadHomeAsync(userId).join();
}

异常需要进入链路

异步任务里吞掉异常,会让调用方误以为任务成功。

更常见的是抛出异常,让 CompletableFuture 异常完成:

java 复制代码
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
    if (remoteFailed()) {
        throw new IllegalStateException("远程服务失败");
    }
    return "OK";
});

然后在链路后面统一处理:

java 复制代码
future.exceptionally(ex -> "fallback");

设置超时

远程接口、数据库、RPC 调用都适合设置超时。

java 复制代码
CompletableFuture<String> future = CompletableFuture
        .supplyAsync(() -> callRemoteService(), executor)
        .orTimeout(2, TimeUnit.SECONDS);

常用 API 汇总

API 作用
ExecutorService.submit(...) 提交任务并返回 Future
Future.get() 阻塞等待结果
Future.get(timeout, unit) 带超时等待结果
Future.cancel(true) 尝试取消任务
CompletableFuture.runAsync(...) 异步执行无返回任务
CompletableFuture.supplyAsync(...) 异步执行有返回任务
CompletableFuture.completedFuture(value) 创建已完成结果
CompletableFuture.failedFuture(error) 创建已失败结果
complete(value) 手动完成结果
completeExceptionally(error) 手动以异常完成
thenApply(...) 转换结果
thenAccept(...) 消费结果
thenRun(...) 完成后执行动作
thenCompose(...) 串行异步编排
thenCombine(...) 合并两个独立任务
allOf(...) 等待全部任务完成
anyOf(...) 等待任意一个任务完成
applyToEither(...) 两个任务谁先成功用谁
exceptionally(...) 异常兜底
handle(...) 成功和异常统一处理
whenComplete(...) 完成后观察结果
orTimeout(...) 超时后异常完成
completeOnTimeout(...) 超时后返回默认值
join() 阻塞获取结果,抛非受检异常
getNow(defaultValue) 不阻塞获取结果

总结

FutureCompletableFuture 都围绕"异步任务结果"展开。

Future 更基础:

text 复制代码
提交任务
拿到 Future
需要结果时 get

CompletableFuture 更适合任务编排:

text 复制代码
异步执行
结果转换
串行依赖
并行合并
异常兜底
超时控制

普通单个异步任务,用 Future 就能解决。

如果一个接口需要同时查多个服务、组合多个结果、设置兜底和超时,CompletableFuture 更顺手。

真正落到工程里,重点不是把所有代码都改成异步,而是把适合并发等待的 I/O 任务拆出来,再配好线程池、超时、异常和监控。

相关推荐
长孙豪翔1 小时前
在.net中读写config文件的各种方法
java·数据库·.net
tachibana21 小时前
hot100 回文链表(234)
java·网络·数据结构·leetcode·链表
可乐ea2 小时前
【Java八股|第10篇】Java 中的包装类和自动拆装箱
java·面试题·包装类·java八股
zfoo-framework2 小时前
mongo最佳实战(from mongo中文社区)
java
深盾科技_Virbox2 小时前
加密狗授权能力选型:从授权模型到全生命周期管理
java·网络·数据库
. . . . .3 小时前
Egg框架深入
java·开发语言
RainCity3 小时前
Java Swing 自定义组件库分享(十三)
java·笔记·后端
livemetee4 小时前
【关于Spring声明式事务】
java·后端·spring
倒流时光三十年4 小时前
Java 内存模型(JMM)通俗解释
java·开发语言