单个执行程序服务中的RejectedExecutionException

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

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. 如果我们无法将任务排队,则尝试添加一个新线程。如果失败,我们知道我们已关闭或饱和,因此拒绝该任务。
  2.   

如果确实如此,execute的文档中为什么没有提到这一点?

  

如果由于执行程序已关闭或已达到其容量而无法提交执行任务,则由当前的RejectedExecutionHandler处理该任务。

坦率地说,我虽然ExecutorService是GC编写的-可达性和范围是不同的东西,并且允许GC清除 无法达到的所有内容;但是有一个Future<?>会强烈引用该服务,因此我将其排除在外。

lidwbbg 回答:单个执行程序服务中的RejectedExecutionException

您写了

  

坦率地说,我虽然ExecutorService是GC编写的-可达性和范围是不同的东西,并且允许GC清除 无法达到的所有内容;但是有一个Future<?>会强烈引用该服务,因此我将其排除在外。

但这实际上是一个非常合理的场景,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

关于期货持有对其创建ExecutorService的引用的假设是错误的。实际类型取决于服务实现,但是对于常见的实现,它将是FutureTask的实例,而该实例没有引用ExecutorService。这也适用于您的情况,在异常消息中也可见。

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

请注意,尽管错误报告仍处于打开状态,但问题实际上已在JDK 11中解决。在那里,ThreadPoolExecutor的基类,FinalizableDelegatedExecutorService类具有DelegatedExecutorService实现看起来像这样:

execute
本文链接:https://www.f2er.com/3158212.html

大家都在问