gpt4 book ai didi

java - 单个执行程序服务中的 RejectedExecutionException

转载 作者:行者123 更新时间:2023-12-04 21:29:21 27 4
gpt4 key购买 nike

在我们的一项服务中,有人添加了这样(简化)的一段代码:

public class DeleteMe {

public static void main(String[] args) {

DeleteMe d = new DeleteMe();
for (int i = 0; i < 10_000; ++i) {
d.trigger(i);
}
}

private Future<?> trigger(int i) {

ExecutorService es = Executors.newSingleThreadExecutor();
Future<?> f = es.submit(() -> {
try {
// some long running task
Thread.sleep(10_000);
} catch (InterruptedException e) {
e.printStackTrace();
}
});

return f;
}
}

这有时会失败:
Exception in thread "main" java.util.concurrent.RejectedExecutionException: Task java.util.concurrent.FutureTask@3148f668 rejected from java.util.concurrent.ThreadPoolExecutor@6e005dc9[Terminated, pool size = 0, active threads = 0, queued tasks = 0, completed tasks = 0]
at java.util.concurrent.ThreadPoolExecutor$AbortPolicy.rejectedExecution(ThreadPoolExecutor.java:2063)
at java.util.concurrent.ThreadPoolExecutor.reject(ThreadPoolExecutor.java:830)
at java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:1379)
at java.util.concurrent.AbstractExecutorService.submit(AbstractExecutorService.java:112)
at java.util.concurrent.Executors$DelegatedExecutorService.submit(Executors.java:678)
at com.erabii.so.DeleteMe.trigger(DeleteMe.java:29)
at com.erabii.so.DeleteMe.main(DeleteMe.java:22)

大多数时候错误是 OutOfMemoryError - 我完全理解。编写代码的人从未调用过 ExecutorService::shutDown ,从而让它活得太久。当然为每个方法调用创建一个单独的执行器服务是不好的,会被改变;但这正是看到错误的原因。

我不明白的一点是为什么 RejectedExecutionException会被抛出,特别是它被抛出 here .

代码注释 there有点道理:

  1. If we cannot queue task, then we try to add a new thread. If it fails, we know we are shut down or saturated and so reject the task.


如果确实如此,那么 execute 的文档怎么来的?不提这个?

If the task cannot be submitted for execution, either because this executor has been shutdown or because its capacity has been reached, the task is handled by the current RejectedExecutionHandler.



坦率地说,最初我认为 ExecutorService是 GC-ed - 可达性和范围是不同的东西,GC 可以清除任何不可达的东西;但有一个 Future<?>这将保持对该服务的强引用,所以我排除了这一点。

最佳答案

你写了

To be frank initially I though that ExecutorService is GC-ed - reachability and scope are different things and GC is allowed to clear anything which is not reachable; but there is a Future<?> that will keep a strong reference to that service, so I excluded this.



但这实际上是一个非常合理的场景,在 JDK-8145304 中有所描述。 .在错误报告的示例中 ExecutorService不保存在局部变量中,但局部变量本身并不能阻止垃圾收集。

注意异常信息
Task java.util.concurrent.FutureTask@3148f668 rejected from  
java.util.concurrent.ThreadPoolExecutor@6e005dc9[Terminated,
pool size = 0, active threads = 0, queued tasks = 0, completed tasks = 0]

支持这一点,作为 ThreadPoolExecutor@6e005dc9 的状态指定为 Terminated .

假设 future 持有对其创建的引用 ExecutorService是错的。实际类型取决于服务实现,但对于普通的,它将是 FutureTask 的实例它没有引用 ExecutorService .这在适用于您的案例的异常消息中也可见。

即使它有引用,创建者也将是实际的 ThreadPoolExecutor ,但它是包装 FinalizableDelegatedExecutorService收集垃圾并调用 shutdown() 的实例在 ThreadPoolExecutor实例(瘦包装器通常是优化代码中过早垃圾收集的良好候选者,它只是绕过包装)。

请注意,虽然错误报告仍处于打开状态,但问题实际上已在 JDK 11 中修复。在那里, FinalizableDelegatedExecutorService 的基类,类(class) DelegatedExecutorService有一个 execute看起来像这样的实现:
public void execute(Runnable command) {
try {
e.execute(command);
} finally { reachabilityFence(this); }
}

关于java - 单个执行程序服务中的 RejectedExecutionException,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58714980/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com