因此 Oracle itself 提出的最佳实践关闭 ExecutorService 的方法如下:
@PreDestroy
public void cleanUp(){
executorService.shutdown();
try {
if (executorService.awaitTermination(TIMEOUT, TimeUnit.MILLISECONDS)) {
executorService.shutdownNow();
}
} catch (InterruptedException e) {
executorService.shutdownNow();
}
}
这也将负责取消任何待处理的任务。我想知道何时上面的代码片段优于以下代码:
private List<Future> tasks = Collections.EMPTY_LIST;
public void doStuff(){
for (Service service : services) {
Future<?> future = executorService.submit(()->service.update(args));
tasks.add(task);
}
}
@PreDestroy
public void cleanUp() {
for (Future task : tasks) {
task.cancel(false);
}
}
后者将允许正在运行的任务完成而不中断它们(tasks.cancel(false)
)。在这种方法中,没有超时,因此任务中的无限循环将阻止应用程序停止。此外,我们还剩下一个仍在运行的执行程序服务:但是,如果确定在已取消的任务完成后无法提交其他任务,我们真的应该关心这个吗?
我最感兴趣的是在人们想要等待作业任务终止/完成然后再进行实际关闭的情况下,什么被认为是最佳实践。为什么我们实际上关心执行程序服务本身的关闭?
恕我直言,拥有一份在应用程序关闭时取消的 future 列表是一种更简洁的解决方案,因为人们可以选择哪些要中断,哪些要等待完成。
对此的任何更详细的见解都非常受欢迎。
这些都是 Spring bean 的一部分,您可以从用于将 cleanUp
方法指定为用于清理任务的关闭 Hook 的 @PotsDestroy
注释中推断出来。执行者服务。
最佳答案
在建议的解决方案中,应在 shutdownNow() 方法调用后调用 wait。
An unused ExecutorService should be shut down to allow reclamation of its resources.
shutdownNow():-
- 将运行状态转换为停止
- 如果已启动,则中断工作线程
- 清空队列 - 删除所有线程
Future.cancel(假):-
- 不中断正在运行的任务
- 如果任务已经完成,则取消尝试将会失败 已经被取消,或者无法取消其他一些 原因。您可能需要额外的逻辑来处理不同的 场景。 shutdownNow() 已经可以处理这些场景。
简短描述和建议:-
实际上,拥有
List
对象本身并不是线程安全的单例 bean 是无状态的。它不应该维持状态 在类(class)层面。即使你将其声明为原型(prototype) bean,它也是 维护 List 不是一个好习惯(List 不是线程安全的) future 。
在内部,执行器服务实现类(例如 ThreadPoolExecutor、ScheduledThreadPoolExecutor 等)负责处理所有线程安全问题。它使用BlockingQueue来克服线程安全问题。此外,它还相应地处理异常和不同的状态。您可能需要了解所有内部结构(即幕后发生的情况)才能提出良好的定制解决方案。
简单且最好的方法是使用执行器服务实现中可用的标准实现(ThreadPoolExecutor、ScheduledThreadPoolExecutor 等)以避免任何不利影响(即内存泄漏、线程安全问题)。
关于java - 关闭执行程序服务(等待终止时)与等待取消已提交任务(使用 Submit 的 future)之间的比较,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52163885/