
在java的并发api中,当任务提交给executorservice后,通常会返回一个future对象。future对象代表了异步计算的结果,并提供了检查计算是否完成、等待计算完成以及获取计算结果的方法。其中,get()方法用于阻塞当前线程,直到任务完成并返回结果。get(long timeout, timeunit unit)方法则提供了超时机制,如果在指定时间内任务未能完成,则会抛出timeoutexception。
需要注意的是,Future.get()方法的超时是针对单个任务的。如果代码中连续调用多个Future.get(),这些调用将是顺序执行的,每个调用都会独立地等待其指定的超时时间。
ExecutorService.awaitTermination(long timeout, TimeUnit unit)方法是ExecutorService生命周期管理中的一个重要组成部分。它通常在调用executorService.shutdown()之后使用。shutdown()方法会平滑地关闭线程池,不再接受新任务,但会允许已提交的任务继续执行。
awaitTermination()方法的作用是阻塞当前线程,直到所有提交的任务都已完成执行,或者直到指定的超时时间已过,或者当前线程被中断。它返回一个布尔值,表示是否所有任务都在超时时间内完成。这个超时是针对整个线程池中所有剩余任务的。
让我们通过以下示例代码来深入分析这两种超时机制的交互:
立即学习“Java免费学习笔记(深入)”;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.*;
public class ExecutorServiceTimeoutExample {
public static void main(String[] args) {
// 创建一个包含2个线程的线程池
ExecutorService executorService = Executors.newFixedThreadPool(2);
List<Callable<String>> tasksList = new ArrayList<>();
// 任务1:模拟一个耗时4分钟的任务
Callable<String> task1 = () -> {
System.out.println("Task 1 started...");
TimeUnit.MINUTES.sleep(4); // 模拟耗时4分钟
System.out.println("Task 1 finished.");
return "Result from Task 1";
};
// 任务2:模拟一个耗时6分钟的任务
Callable<String> task2 = () -> {
System.out.println("Task 2 started...");
TimeUnit.MINUTES.sleep(6); // 模拟耗时6分钟
System.out.println("Task 2 finished.");
return "Result from Task 2";
};
tasksList.add(task1);
tasksList.add(task2);
List<Future<String>> futures = null;
try {
// 提交所有任务并获取Future列表
futures = executorService.invokeAll(tasksList);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
System.err.println("invokeAll interrupted.");
return;
}
String result1 = null;
String result2 = null;
try {
// 尝试获取第一个任务的结果,设置5分钟超时
System.out.println("Attempting to get result for Task 1 with 5 min timeout...");
result1 = futures.get(0).get(5, TimeUnit.MINUTES);
System.out.println("Result 1: " + result1);
// 尝试获取第二个任务的结果,设置5分钟超时
System.out.println("Attempting to get result for Task 2 with 5 min timeout...");
result2 = futures.get(1).get(5, TimeUnit.MINUTES);
System.out.println("Result 2: " + result2);
} catch (InterruptedException | ExecutionException | TimeoutException e) {
System.err.println("Error getting task result: " + e.getMessage());
} finally {
// 关闭ExecutorService
executorService.shutdown();
System.out.println("ExecutorService shutdown initiated.");
try {
// 等待ExecutorService终止,设置30秒超时
System.out.println("Awaiting termination of ExecutorService with 30 sec timeout...");
if (!executorService.awaitTermination(30, TimeUnit.SECONDS)) {
System.out.println("ExecutorService did not terminate in 30 seconds. Forcing shutdown...");
executorService.shutdownNow(); // 强制关闭
} else {
System.out.println("ExecutorService terminated successfully.");
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
System.err.println("awaitTermination interrupted.");
}
}
}
}在上述示例中:
executorService.invokeAll(taskList)提交了两个任务。由于线程池大小为2,这两个任务会并行执行。
futures.get(0).get(5, TimeUnit.MINUTES):
futures.get(1).get(5, TimeUnit.MINUTES):
executorService.shutdown():
executorService.awaitTermination(30, TimeUnit.SECONDS):
结论:
实际的总等待时间是累加的。在最坏的情况下,如果task1和task2都分别耗时接近或超过5分钟:
因此,实际的总等待时间可能高达 10 分钟 30 秒。awaitTermination的30秒超时并不会覆盖或缩短Future.get()的超时时间,它们是两个独立的、顺序发生的等待阶段。
Future.get()的超时是针对单个任务的阻塞等待,且在代码中是顺序执行的。ExecutorService.awaitTermination()的超时是针对整个线程池在shutdown()后所有剩余任务的最终等待。两者是累加关系,而非覆盖关系。正确理解它们的行为模式,对于编写健壮、高效的Java并发程序至关重要。在设计并发流程时,应仔细考虑任务的执行顺序、结果获取方式以及线程池的生命周期管理,以避免不必要的长时间阻塞或资源泄露。
以上就是Java并发:Future.get()与ExecutorService.awaitTermination()的超时机制解析的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号