2011-10-17 31 views
17

我正在開發一款在Android NDK中運行大多數本機代碼的社交遊戲。遊戲中有3個主要NDK並行線程:將任務提交給線程池給出RejectedExecutionException

  1. 遊戲線程
  2. 服務器通信線程
  3. 主渲染線程(通過Renderer.onRender稱呼)

除此之外,關於java我們使用的是AdWhirl,它通過自己的ScheduledExecutorService產生自己的線程,但是我們用try-catch塊將每個調用都打包到「計劃」,「提交」,「發佈」,「開始」等,以捕獲RejectedExecutionException。然而,恐怖的RejectedExecutionException仍然出現在我們提交的每個新版本中。

來自Android Market的堆棧跟蹤對我來說幾乎沒有任何線索,我們的QA部門也發現難以確定問題,因爲它在測試過程中很難發生(只有我們的用戶報告崩潰)。它只會影響我們的用戶的一小部分,但它仍然

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) 

回答

13

你必須檢查你的代碼,它是創造遠(與大量安裝的基礎比較小的部分)是每週超過7000崩潰太多的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

+0

我明白了!感謝您的線索,是否有增加尺寸或有任何限制? 在我們的代碼中,我們沒有使用這麼多的AsyncTask,但我們使用ScheduledExecutorService和其他執行程序並指定較大的線程池。有相當多的執行者,對所有執行者的線程總數是否有任何硬性限制? 如果組合應用程序範圍內的線程數限制,我將不得不檢查...在某個時間點,我們的應用程序正在運行超過20個線程... – Zennichimaro

+0

根據Diane沒有硬性限制,但這是保持十年的最佳做法。您可以設置[最大池大小](http://developer.android.com/reference/java/util/concurrent/ThreadPoolExecutor.html#setMaximumPoolSize(int)) – Reno

+1

因爲我已經重寫了應用程序以使用較小的Runnable和AsyncTask ,它現在正在工作,並且沒有看到任何RejectedExecutionException。感謝Reno和Diane! – Zennichimaro

17

,而你當然應該儘量讓事情儘可能有效,不存在任意限制你「允許」運行的線程數量,這完全取決於你如何構建你的代碼。

ThreadPoolExecutor類是極其記錄良好,是您看到的問題的起源。我會recommend reading through it,檢查出

首先我猜你用Ant構建這一點,並沒有使用到javac節點上的這些參數:

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

或許到那時你混淆顯然使用的原因什麼,通常會是一個堆棧跟蹤的最重要組成部分,而不是簡單的輸出:

c.onProgressUpdate(Unknown Source) 

這是當前ICS 4.0.4源的ThreadPoolExecutor。AbortPolicy,你可以看到它基本上是一個包羅萬象的那個總是拋出異常:

/** 
* 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()); 
    } 
} 

此外,你會發現的DefaultHandler在ThreadPoolExecutor的頂部聲明:

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(); 
     } 
    } 
} 
+0

我明白了,感謝您的指導! 我工作和建設日食,我沒有修改任何的javac params,所以除非默認javac調試設置爲true,它不會被包含在我的構建。 是的,這是一個棘手和耗時的問題來解決..我還沒有找到任何解決方案,並且從此轉向谷歌的AdMob調解取代AdWhirl。這成功地避免了拒絕執行問題:當我有時間時,我會再次看看它。謝謝! – Zennichimaro

+0

@Justin我的回答已經過時了,你是對的(增加投票等)。此外,這不是一個論壇,我編輯了噪音,[請閱讀常見問題](http://stackoverflow.com/questions/how-to-answer)。 – Reno