【问题标题】:Submitting tasks to a thread-pool gives RejectedExecutionException将任务提交到线程池会产生 RejectedExecutionException
【发布时间】:2011-10-17 01:47:23
【问题描述】:

我正在开发一款在 Android NDK 中运行大部分原生代码的社交游戏。游戏有 3 个主要的 ndk pthreads:

  1. 一个游戏线程
  2. 服务器通信线程
  3. 主渲染线程(通过 Renderer.onRender 调用)

除此之外,在 java 方面,我们使用 AdWhirl,它通过自己的 ScheduledExecutorService 生成自己的线程,但我们已经包装了对“schedule”、“submit”、“post”、“start”等的每个调用使用 try-catch 块捕获RejectedExecutionException。然而,可怕的RejectedExecutionException 仍然出现在我们提交的每个新版本中。

Android Market 的堆栈跟踪几乎没有给我留下任何线索,我们的 QA 部门也发现很难查明问题,因为它在测试期间几乎没有发生(只有我们的用户报告了崩溃)。它只影响我们的一小部分用户,但每周仍然有超过 7,000 次崩溃(与大量安装基数相比,这是一小部分)

java.util.concurrent.RejectedExecutionException
at         java.util.concurrent.ThreadPoolExecutor$AbortPolicy.rejectedExecution(ThreadPoolExecutor.java:1876)
at java.util.concurrent.ThreadPoolExecutor.reject(ThreadPoolExecutor.java:774)
at java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:1295)
at android.os.AsyncTask.execute(AsyncTask.java:394)
at c.onProgressUpdate(Unknown Source)
at android.os.AsyncTask$InternalHandler.handleMessage(AsyncTask.java:432)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:123)
at android.app.ActivityThread.main(ActivityThread.java:4632)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:521)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:858)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)
at dalvik.system.NativeStart.main(Native Method)

    java.util.concurrent.RejectedExecutionException: pool=128/128, queue=10/10
at java.util.concurrent.ThreadPoolExecutor$AbortPolicy.rejectedExecution(ThreadPoolExecutor.java:1961)
at java.util.concurrent.ThreadPoolExecutor.reject(ThreadPoolExecutor.java:794)
at java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:1315)
at android.os.AsyncTask.execute(AsyncTask.java:394)
at c.onProgressUpdate(Unknown Source)
at android.os.AsyncTask$InternalHandler.handleMessage(AsyncTask.java:432)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:123)
at android.app.ActivityThread.main(ActivityThread.java:3691)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:507)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:847)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:605)
at dalvik.system.NativeStart.main(Native Method)

    java.util.concurrent.RejectedExecutionException
at java.util.concurrent.ThreadPoolExecutor$AbortPolicy.rejectedExecution(ThreadPoolExecutor.java:1876)
at java.util.concurrent.ThreadPoolExecutor.reject(ThreadPoolExecutor.java:774)
at java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:1295)
at android.os.AsyncTask.execute(AsyncTask.java:394)
at c.onProgressUpdate(Unknown Source)
at android.os.AsyncTask$InternalHandler.handleMessage(AsyncTask.java:432)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:123)
at android.app.ActivityThread.main(ActivityThread.java:4627)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:521)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:858)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)
at dalvik.system.NativeStart.main(Native Method)

【问题讨论】:

    标签: android multithreading android-ndk


    【解决方案1】:

    虽然您当然应该尽量保持效率,但“允许”运行的线程数没有任意限制,这完全取决于您如何构建代码。

    ThreadPoolExecutor非常有据可查,并且是您看到的问题的根源。我会recommend reading through it,看看

    首先我猜你是用 Ant 构建的,并且没有在你的 javac 节点上使用这些参数:

    <javac debug="true" debuglevel="lines,vars,source" />
    

    无论是那个还是您显然使用的混淆器都是堆栈跟踪中通常最重要的部分只是简单地输出的原因:

    c.onProgressUpdate(Unknown Source)
    

    这是 ThreadPoolExecutor.AbortPolicy 的当前 ICS 4.0.4 源代码,您可以看到它基本上是一个总能抛出异常的包罗万象:

     /**
     * A handler for rejected tasks that throws a
     * {@code RejectedExecutionException}.
     */
    
    public static class AbortPolicy implements RejectedExecutionHandler {
        /**
         * Creates an {@code AbortPolicy}.
         */
        public AbortPolicy() { }
    
        /**
         * Always throws RejectedExecutionException.
         *
         * @param r the runnable task requested to be executed
         * @param e the executor attempting to execute this task
         * @throws RejectedExecutionException always.
         */
        public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
            throw new RejectedExecutionException("Task " + r.toString() +
                                                 " rejected from " +
                                                 e.toString());
        }
    }
    

    此外,您会发现在 ThreadPoolExecutor 顶部声明的 defaultHandler:

    private static final RejectedExecutionHandler defaultHandler = new AbortPolicy();
    

    最后,如果您查看 ThreadPoolExecutor 的默认构造函数:

    public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory) {
        this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
             threadFactory, defaultHandler);
    }
    

    您会看到它正在使用它的 AbortPolicy 类来实例化自己,这是它的默认 RejectedExecutionHandler

    ThreadPoolExecutor 还包括其他几个您可以设置为默认值的RejectedExecutionHandler 子类,例如:

      /**
     * A handler for rejected tasks that silently discards the
     * rejected task.
     */
    public static class DiscardPolicy implements RejectedExecutionHandler {
        /**
         * Creates a {@code DiscardPolicy}.
         */
        public DiscardPolicy() { }
    
        /**
         * Does nothing, which has the effect of discarding task r.
         *
         * @param r the runnable task requested to be executed
         * @param e the executor attempting to execute this task
         */
        public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
        }
    }
    

    其他 3 个ThreadPoolExecutor 构造函数包含一个处理程序选项,因此您可以使用不同的处理程序创建它的实例,或者创建自己的子类,类似于:

    package com.justinbuser;
    
    import java.util.concurrent.BlockingQueue;
    import java.util.concurrent.Executors;
    import java.util.concurrent.RejectedExecutionException;
    import java.util.concurrent.RejectedExecutionHandler;
    import java.util.concurrent.ThreadFactory;
    import java.util.concurrent.ThreadPoolExecutor;
    import java.util.concurrent.TimeUnit;
    
    public class NoThrowThreadPool extends ThreadPoolExecutor {
    
        private static final RejectedExecutionHandler defaultHandler = new AdoptPolicy();
    
        public NoThrowThreadPool(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue) {
            super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, Executors.defaultThreadFactory(), defaultHandler);
            setRejectedExecutionHandler(defaultHandler);
        }
    
        public NoThrowThreadPool(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue,
                ThreadFactory threadFactory) {
            super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory, defaultHandler);
        }
    
        public NoThrowThreadPool(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue,
                RejectedExecutionHandler handler) {
            super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, Executors.defaultThreadFactory(), defaultHandler);
        }
    
        public NoThrowThreadPool(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue,
                ThreadFactory threadFactory, RejectedExecutionHandler handler) {
            super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory, defaultHandler);
        }
    
        public static class AdoptPolicy extends ThreadPoolExecutor.AbortPolicy {
    
            @Override
            public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
                new RejectedExecutionException("Task " + r.toString() + " rejected from " + e.toString()).printStackTrace();
            }
        }
    }
    

    【讨论】:

    • 我明白了,感谢您的指导!我正在 Eclipse 上工作和构建,我没有修改任何 javac 参数,所以除非默认情况下 javac debug 设置为 true,否则它不会包含在我的构建中。是的,这是一个棘手且耗时的问题要解决。我也没有找到任何解决方案,并且已经转向 Google 的 AdMob 中介来替代 AdWhirl。这成功避免了 RejectedExecution 问题 :P 我有空再看一遍。谢谢!
    • @Justin 我的回答已经过时了,你是对的(投票赞成等)。另外这不是论坛,我已经删除了噪音,please read the FAQ's
    【解决方案2】:

    您必须检查您的代码,它创建的 AsyncTask 数量超出了允许范围。

    policy 设置为

    private static final int CORE_POOL_SIZE = 1;
    private static final int MAXIMUM_POOL_SIZE = 10;
    private static final int KEEP_ALIVE = 10; 
    

    注意:这在不同的Android版本中会有所不同

    【讨论】:

    • 我明白了!谢谢你的线索,有没有增加尺寸或有任何限制?在我们的代码中,我们并没有使用这么多的 AsyncTask,而是使用了 ScheduledExecutorService 和其他执行器,并指定了更大的线程池。这些执行器有很多,所有执行器上的线程总数是否有硬性限制?如果有一个组合应用程序范围的线程数限制,我将不得不检查......在某个时间点,我们的应用程序正在运行超过 20 个线程......
    • Diane 没有硬性限制,但最好保持在 10 以内。您可以设置maximum pool size
    • 因为我已经重写了应用程序以使用较少的 Runnable 和 AsyncTask,它现在正在工作并且不再看到任何 RejectedExecutionException。谢谢雷诺和黛安!
    • 请注意不要将线程池大小与队列大小混淆。 threadpoolsize 是一次可以运行以服务队列的最大线程数。队列大小是队列可以得到多长时间。导致被拒绝执行异常的原因是队列大小,而不是池大小。从 2.1 开始,队列大小确实是 10,尽管池大小实际上是 128:grepcode.com/file/repository.grepcode.com/java/ext/…
    【解决方案3】:

    当 Executor 已关闭,以及 Executor 对最大线程和工作队列容量都使用有限边界且已饱和时,在方法 execute(java.lang.Runnable) 中提交的新任务将被拒绝。

    http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/6-b14/java/util/concurrent/ThreadPoolExecutor.java

    【讨论】:

      猜你喜欢
      • 2021-12-28
      • 2014-09-02
      • 1970-01-01
      • 2016-06-24
      • 1970-01-01
      • 1970-01-01
      • 2016-03-15
      • 1970-01-01
      • 2012-12-28
      相关资源
      最近更新 更多