CompletionService和CompletableFuture的区别是什么?

1,285 阅读2分钟

CompletionService和CompletableFuture都是用于管理异步任务的工具,但是有以下不同点:

  • CompletionService是一个接口,它提供了一种将生产者提交的任务和消费者获取的结果进行解耦的机制。它有一个内部的阻塞队列,用于存储已完成的任务的Future对象。消费者可以调用take()或poll()方法从队列中获取已完成的任务的Future对象,而不用关心任务的提交顺序。这样可以提高效率,避免不必要的等待。
  • CompletableFuture是一个类,它实现了Future和CompletionStage两个接口。它提供了一种基于回调的方式来处理异步任务的结果,可以将多个异步任务以流水线或者组合的方式连接起来,形成一个复杂的异步计算过程。它还支持异常处理、超时控制、异步执行等功能。
  • CompletionService更适合于批量提交异步任务,并且只关心任务的结果,而不关心任务之间的依赖关系。例如,同时从多个数据源查询数据,返回任意一个可用的结果即可。
  • CompletableFuture更适合于构建复杂的异步计算流程,并且可以灵活地控制任务之间的依赖关系和执行顺序。例如,根据用户输入查询商品信息,然后根据商品信息查询库存和价格,最后显示给用户。

下面是一些使用示例:

// 使用CompletionService批量提交异步任务,并按完成顺序获取结果  
ExecutorService executor = Executors.newFixedThreadPool(10);  
CompletionService<String> completionService = new ExecutorCompletionService<>(executor);  
// 提交10个异步任务  
for (int i = 0; i < 10; i++) {  
final int index = i;  
completionService.submit(() -> {  
// 模拟不同的执行时间  
Thread.sleep((10 - index) * 100);  
return "Task " + index;  
});  
}  
// 按完成顺序获取结果  
for (int i = 0; i < 10; i++) {  
Future<String> future = completionService.take();  
System.out.println(future.get());  
}  
executor.shutdown();  
  
// 使用CompletableFuture构建复杂的异步计算流程,并处理异常  
// 模拟一个查询商品信息的方法,可能抛出异常

public static CompletableFuture<String> queryProduct(String input) {  
return CompletableFuture.supplyAsync(() -> {  
if (input == null || input.isEmpty()) {  
throw new IllegalArgumentException("Invalid input");  
}  
// 模拟查询过程  
return "Product: " + input;  
});  
}  
// 模拟一个查询库存的方法  
public static CompletableFuture<Integer> queryStock(String product) {  
return CompletableFuture.supplyAsync(() -> {  
// 模拟查询过程  
return (int) (Math.random() * 100);  
});  
}  
// 模拟一个查询价格的方法  
public static CompletableFuture<Double> queryPrice(String product) {  
return CompletableFuture.supplyAsync(() -> {  
// 模拟查询过程  
return Math.random() * 1000;  
});  
}  
// 根据用户输入查询商品信息,然后根据商品信息查询库存和价格,最后显示给用户  
public static void display(String input) {  
// 异步查询商品信息  
CompletableFuture<String> productFuture = queryProduct(input);  
// 异步查询库存和价格,组合两个结果  
CompletableFuture<String> resultFuture = productFuture.thenCombine(queryStock(productFuture.join()), (product, stock) -> product + ", Stock: " + stock)  
.thenCombine(queryPrice(productFuture.join()), (result, price) -> result + ", Price: " + price);  
// 异步显示结果,处理异常  
resultFuture.thenAccept(System.out::println)  
.exceptionally(ex -> {  
System.out.println("Error: " + ex.getMessage());  
return null;  
});  
}

参考: