当前位置: 首页 > 面试题库 >

单个执行程序服务中的RejectedExecutionException

姚向晨
2023-03-14
问题内容

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

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会被抛出,特别是在这里被抛出。

那里的代码注释有一定道理:

  1. 如果我们无法将任务排队,则尝试添加一个新线程。如果失败,我们知道我们已关闭或已 饱和 ,因此拒绝该任务。

如果确实如此,那么execute为何文档中没有提及呢?

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

坦率地说,我虽然是ExecutorService由GC编写的-可访问性和范围是不同的东西,并且允许GC清除所有 无法
访问的内容;但是有一个Future<?>可以对该服务有很强的参考意义,因此我将其排除在外。


问题答案:

你写了

坦率地说,我虽然是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实例将被垃圾收集并调用shutdown()ThreadPoolExecutor实例(瘦包装器通常是绕过包装的优化代码中过早垃圾收集的良好候选者)。

请注意,尽管错误报告仍处于打开状态,但该问题实际上已在JDK
11中解决。在那里,的基FinalizableDelegatedExecutorServiceDelegatedExecutorService具有一个execute类似于以下内容的实现:

public void execute(Runnable command) {
    try {
        e.execute(command);
    } finally { reachabilityFence(this); }
}


 类似资料:
  • 我正纠结于这样一个问题:一个可调用的任务在顺利地工作了一天的大部分时间后,被放入Java单线程执行器中,而很明显GET从来没有被执行过。提交新任务的后续调用失败,似乎已死。此时,生成任务的客户端将停止服务,直到流程可以重新启动,这在工作时间是不可能的。 一些背景:多个高吞吐量生产者线程将其任务放置到自己专用的上并立即返回。低延迟对于生产者线程非常重要。在生产者线程和执行器线程之间存在一对一的关系。

  • 如果我的方法被多次调用,并且它仍然在从以前的线程运行run方法,会发生什么?它会启动同样多的线程还是会等待一个线程完成后再启动另一个线程?

  • 问题内容: 我正在处理一个系统,该系统在其自己的JVM中为每个客户运行Java应用程序。现在,我们有大约六个专用服务器,它们总共运行近100个JVM,以及用于管理这些JVM的自定义脚本集。此设置实际上已经表明了它的年龄:管理许多JVM已成为监视/管理的噩梦,并且我们一直在处理堆大小调整问题。我们想采用一种更现代的方法,并在每台物理计算机的单个应用服务器中运行一堆应用程序。但是,将应用程序保持隔离确

  • 我使用org.eclipse.jetty.server.handler.请求日志处理程序 但此处理程序日志仅: 0:0:0:0:0:0:0:1--[04/θών/2014:08:16:27 0000]“GET/CardTransfer-0.1.0-SNAPSHOT/transf HTTP/1.1“200 22”-“Mozilla/5.0(Windows NT 6.1;WOW64;rv:29.0)G

  • 野姑娘。如果我编写了这个包装器,我可以按照上面描述的方式调用包装器: XML::XPath。可以使用此包装: XML::XPath中的返回太多噪声、和。 返回与类似的噪声。 返回。 返回所需的内容,但仅针对第一个匹配项。 对于几乎满足这个问题的另一个解决方案,这里有一个XSLT,可以用来计算任意XPath表达式(需要XSLT处理器中的dyn:evaluate支持): 使用。

  • 这种方法的Java博士说 如果需要,最多等待给定的时间完成计算,然后检索其结果(如果可用)。 参数: 超时等待的最长时间 unit超时参数的时间单位 根据我的理解,我们对强加了一个超时,我们提交给,这样,我的将在指定的时间(超时)过去后中断 但是根据下面的代码,似乎超过了超时时间(2秒),我真的很难理解这一点。谁能给我指一下正确的路吗?