答案:ExecutorCompletionService通过将任务结果存入阻塞队列,使结果按完成顺序而非提交顺序被处理。它结合了Executor和BlockingQueue的优点,在任务执行时间不确定的场景下,避免了因等待慢任务而阻塞后续已完成任务结果的获取。与直接使用ExecutorService的Future.get()相比,后者必须按提交顺序阻塞等待,而CompletionService提供take()方法实时获取最先完成的任务结果,提升响应速度和资源利用率。典型应用场景包括爬虫请求、渐进式数据处理和资源敏感型任务。使用时需注意异常处理(如CancellationException)、手动管理线程池生命周期、正确设置结果获取循环终止条件,并权衡其额外队列开销。代码示例展示了提交10个异步任务并按完成顺序打印结果的过程,最后安全关闭线程池。

在Java中,当你需要并行执行多个任务,并且希望以任务完成的顺序(而不是提交的顺序)来处理结果时,
ExecutorCompletionService
Executor
BlockingQueue
使用
ExecutorCompletionService
take()
ExecutorService
Future
get()
下面是一个基本的使用示例:
import java.util.concurrent.*;
import java.util.Random;
public class CompletionServiceDemo {
public static void main(String[] args) {
// 1. 创建一个线程池,作为ExecutorCompletionService的底层执行器
// 我个人喜欢用FixedThreadPool,因为它能控制并发度,避免资源耗尽
ExecutorService executor = Executors.newFixedThreadPool(5);
// 2. 创建ExecutorCompletionService实例,将线程池传入
// 这一步是关键,它将ExecutorService包装起来,提供了更高级的完成服务
CompletionService<String> completionService = new ExecutorCompletionService<>(executor);
int numberOfTasks = 10;
Random random = new Random();
// 3. 提交Callable任务
// 每个任务模拟不同的执行时间
for (int i = 0; i < numberOfTasks; i++) {
final int taskId = i;
completionService.submit(() -> {
long sleepTime = random.nextInt(3000) + 500; // 模拟0.5到3.5秒的执行时间
System.out.println("任务 " + taskId + " 开始执行,预计耗时 " + sleepTime + "ms");
Thread.sleep(sleepTime);
return "任务 " + taskId + " 完成,耗时 " + sleepTime + "ms";
});
}
// 4. 获取并处理已完成的任务结果
// take()方法会阻塞,直到有任务完成并返回其Future
System.out.println("\n--- 开始获取任务结果 ---\n");
for (int i = 0; i < numberOfTasks; i++) {
try {
// take()方法阻塞等待第一个完成的任务
Future<String> future = completionService.take();
// get()方法不会阻塞,因为任务已经完成
String result = future.get();
System.out.println("处理结果: " + result);
} catch (InterruptedException e) {
// 当前线程被中断,通常意味着需要停止处理
Thread.currentThread().interrupt();
System.err.println("等待任务完成时被中断: " + e.getMessage());
} catch (ExecutionException e) {
// 任务执行过程中抛出了异常
System.err.println("任务执行异常: " + e.getMessage());
// 可以进一步检查e.getCause()获取原始异常
} catch (CancellationException e) {
// 任务被取消了
System.err.println("任务被取消: " + e.getMessage());
}
}
// 5. 关闭线程池
// 这一步很重要,否则程序可能不会退出
executor.shutdown();
try {
if (!executor.awaitTermination(60, TimeUnit.SECONDS)) {
executor.shutdownNow(); // 如果无法在指定时间内关闭,则强制关闭
}
} catch (InterruptedException e) {
executor.shutdownNow();
Thread.currentThread().interrupt();
}
System.out.println("\n所有任务处理完毕,线程池已关闭。");
}
}ExecutorCompletionService
ExecutorService
Future
我记得刚开始接触多线程的时候,总是习惯性地把所有
Future
get()
ExecutorCompletionService
立即学习“Java免费学习笔记(深入)”;
具体来说,当你直接向
ExecutorService
submit()
Future
Future
Future
get()
future.get()
future.get()
而
ExecutorCompletionService
Future
completionService.take()
Future
Future
ExecutorCompletionService
我个人在做一些爬虫项目或者数据处理管道时,特别喜欢用它。想象一下,你发出了几百个HTTP请求,有些服务器响应快,有些慢。如果我用传统的
future.get()
CompletionService
具体来说,以下几种情况,你真的应该考虑它:
ExecutorCompletionService
take()
CompletionService
take()
CompletionService
ExecutorCompletionService
我曾经犯过一个错误,就是忘记处理
CancellationException
Future
get()
Future
get()
try-catch
InterruptedException
ExecutionException
CancellationException
除了异常处理,还有一些点值得我们留意:
Future.cancel(true)
CompletionService
Future
take()
Future
get()
CancellationException
future.get()
InterruptedException
ExecutionException
CancellationException
ExecutorService
ExecutorCompletionService
ExecutorService
executor.shutdown()
try-finally
take()
ExecutorCompletionService
总的来说,
ExecutorCompletionService
以上就是Java中ExecutorCompletionService使用方法的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号