在我们的一项服务中,有人添加了(简化)这样的一段代码:
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有意义:
- 如果我们无法将任务排队,则尝试添加一个新线程。如果失败,我们知道我们已关闭或饱和,因此拒绝该任务。
如果确实如此,execute
的文档中为什么没有提到这一点?
如果由于执行程序已关闭或已达到其容量而无法提交执行任务,则由当前的RejectedExecutionHandler处理该任务。
坦率地说,我虽然ExecutorService
是GC编写的-可达性和范围是不同的东西,并且允许GC清除 无法达到的所有内容;但是有一个Future<?>
会强烈引用该服务,因此我将其排除在外。