源碼閱讀-AsyncTask-任務調度

0. 相關資料

    源碼地址
    android.os.AsyncTask
    java.util.concurrent.Executors

    參考文章
    Android實戰技巧:深入解析AsyncTask
    ThreadPoolExecutor Java文檔
    Executors Java文檔
    java中關鍵字volatile的作用

1. AsyncTask#execute與AsyncTask#executeOnExecutor

    public static void execute(Runnable runnable)
    public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec, Params... params)

    AsyncTask#execute本質是調用THREAD_POOL_EXECUTOR,通過synchronized來保證線程池中的任務是一個一個調用的。
    AsyncTask#executeOnExecutor可以指定一個Execurtor來進行任務調度。

    AsyncTask擁有2個內置Executor,Executors擁有4個內置的Executor,這6個Executor都可以在AsyncTask#executeOnExecutor上面使用。一個Executor最多能執行多少個任務?這些Executor有什麼區別?如何自定義一個Executor?這些問題是本文關注的重點。

2. ThreadPoolExecutor的構造

    上面說的六個Executor都是ThreadPoolExecutor,區別是構造對象的時候,使用的參數不同。下面說明ThreadPoolExecutor的構造函數。
    ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue)

    ThreadPoolExecutor Java文檔的描述如下:
    corePoolSize - 池中所保存的線程數,包括空閒線程。
    maximumPoolSize - 池中允許的最大線程數。
    keepAliveTime - 當線程數大於核心時,此爲終止前多餘的空閒線程等待新任務的最長時間。
    unit - keepAliveTime 參數的時間單位。
    workQueue - 執行前用於保持任務的隊列。此隊列僅保持由 execute 方法提交的 Runnable 任務。

    另外文檔中描述了corePoolSize與maximumPoolSize的關係:如果運行的線程多於 corePoolSize 而少於 maximumPoolSize,則僅當隊列滿時才創建新線程。

    我的理解是這樣的:
    如果運行的任務數量小於corePoolSize ,那麼新的任務會立即執行。
    如果任務數量大於了corePoolSize ,那麼會放到workQueue中,如果workQueue滿了,但是任務數量沒有超過maximumPoolSize ,那麼創建一個新線程來執行。
    如果workQueue滿了,池中任務數量已經達到maximumPoolSize,那麼會執行ThreadPoolExecutor的錯誤處理機制,默認是丟棄該任務。

3. AsyncTask內置Executor

    public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
    public static final Executor THREAD_POOL_EXECUTOR
            = new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE,
                    TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);
    SERIAL_EXECUTOR實際上調用THREAD_POOL_EXECUTOR,只是通過synchronized來保證線程池中的任務是一個一個調用的。AsyncTask#execute就是使用SERIAL_EXECUTOR,因此AsyncTask#execute在執行的時候是一個一個執行的。注意SERIAL_EXECUTOR 是靜態的,所以無論在哪個線程中,使用AsyncTask#execute執行任務,任務都在一起執行。

    THREAD_POOL_EXECUTOR,corePoolSize=5,maximumPoolSize=128,keepAliveTime =1,keepAliveTime =秒,workQueue= LinkedBlockingQueue<Runnable>(10)
    可以有5個線程可以同時運行
    超過5個之後的線程會放到workQueue
    隊列中最多放10個,超過之後,直接創建新的線程直接執行
    最多一共能放128個線程,超過128個之後的線程會被丟棄

4. Executors的內置Executor

    Executors#newCachedThreadPool()

    public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());
    }
    SynchronousQueue是不存儲元素的阻塞隊列。由於corePoolSize=0,每次新來一個任務,都會放到workQueue中,但是SynchronousQueue是不存儲元素的,所以會立即開啓線程執行。由於maximumPoolSize=Integer.MAX_VALUE,執行的線程數量是沒有上限的。文檔中說,對於執行很多短期異步任務的程序而言,這些線程池通常可提高程序性能。

    Executors#newFixedThreadPool()

  public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
  }

    池中最多可以同時運行nThreads個線程,多餘的任務都放在workQueue中,workQueue沒有上限,可以存儲任意多個任務。

    Executors#newSingleThreadExecutor()

    public static ExecutorService newSingleThreadExecutor() {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>()));
    池中只能運行一個線程,其他任務都放在workQueue中。相當於Executors#newFixedThreadPool(1)。

    Executors#newSingleThreadScheduledExecutor

    public static ScheduledExecutorService newSingleThreadScheduledExecutor()

    創建一個單線程執行程序,它可安排在給定延遲後運行命令或者定期地執行。


     這裏主要關注兩個Executor,Executors#newCachedThreadPool()和Executors#newFixedThreadPool()。



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章