Java虚拟线程助Future切换CompletableFuture


Java 8开始有个叫CompletableFuture的好东西,它能让你用很简单的写法来处理那些耗时的操作(比如查数据库或者调外部接口)。比如你可以这样写:

java
// 先查用户Bob的信息
// 查到后自动去查他的订单
// 最后把订单打印出来
CompletableFuture.supplyAsync(() -> getCustomerFromDb("Bob"))
    .thenApply(c -> getOrdersForCustomer(c.id()))
    .thenAccept(o -> System.out.println("Bob's orders: " + o));

但是呢,Java里很多老代码和第三方库还在用老式的Future,这玩意有两个缺点:
1. 必须手动调用get()方法才能拿到结果,而且会卡住当前线程
2. 要么就得不停地用isDone()检查任务完没完成,很麻烦

有人想了个办法把Future转成CompletableFuture:

java
public CompletableFuture toCompletableFuture(Future future) {
  return CompletableFuture.supplyAsync(() -> {
    try {
      return future.get(); // 这里还是会卡住线程
    } catch (Exception e) {
      throw new CompletionException(e);
    }
  });
}

但这个办法只是把卡线程的问题转移了地方,还是在后台卡住了一个线程。

后来Java 21出了虚拟线程这个黑科技,这种线程特别轻量,卡住也没关系。所以我们可以这样改进:

java
public CompletableFuture toCompletableFuture(Future future) {
  CompletableFuture completable = new CompletableFuture();
  
  // 开个虚拟线程专门等着拿结果
  Thread.ofVirtual().start(() -> {
    try {
      completable.complete(future.get());
    } catch (Exception e) {
      completable.completeExceptionally(e);
    }
  });
  
  return completable;
}

这样既不会卡住重要线程,又不用频繁检查,完美解决了老代码和新写法之间的兼容问题。虚拟线程就像不要钱的工人,可以随便开很多个,专门用来干这种等着的工作最合适了。