【发布时间】:2020-03-02 01:00:06
【问题描述】:
在我们的一项服务中,有人添加了这样(简化的)一段代码:
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。
代码 cmets there 有点意思:
- 如果我们不能排队任务,那么我们尝试添加一个新线程。如果它失败了,我们知道我们已经关闭或饱和,因此拒绝任务。
如果真的是这样,execute的文档怎么没有提到这个?
如果任务无法提交执行,要么是因为这个执行器已经关闭,要么是因为它的容量已经达到,任务由当前的 RejectedExecutionHandler 处理。
坦率地说,最初我虽然 ExecutorService 是 GC-ed - 可达性和范围是不同的东西,并且允许 GC 清除任何 不 可达的东西;但是有一个Future<?> 会保留对该服务的强烈引用,所以我排除了这个。
【问题讨论】:
-
“但是有一个
Future<?>将保持对该服务的强引用” -Future只是由该方法返回,但没有在其他任何地方使用/存储。也许 JVM 看到了这一点并确定它无法访问? -
@JacobG。如前所述 - 这是简化的。 它是在调用者中使用的,当然。
-
@JacobG。还要注意
pool size = 0, active threads = 0, queued tasks = 0, completed tasks = 0- 这意味着 nothing 尚未提交到队列中,这发生在主线程中。所以主线程(不是池中的那个)必须将任务放入队列中,然后然后让执行到一个不同的线程——这甚至没有发生在这里。 -
如果您使用
newFixedThreadPool(1)而不是newSingleThreadExecutor(),您是否会遇到相同的行为?
标签: java java-8 executorservice