Android中的線程池ThreadPoolExecutor

線程池主要相關的有如下幾個類:
1、Executor
最底層的接口,規定了一個void execute(Runnable command)接口,用於執行線程任務;

2、ExecutorService、AbstractExecutorService
ExecutorService抽象類繼承Executor接口,AbstractExecutorService抽象類繼承ExecutorService。這兩個抽象類對線程池做了進一步封裝;

3、ThreadPoolExecutor
ThreadPoolExecutor是就是線程池最終的實現了,繼承自AbstractExecutorService抽象類;

4、Executors
Executors是一個快速創建ThreadPoolExecutor的工具類。提供了多個靜態方法用於創建各種類型的線程池,其本質也是調用new ThreadPoolExecutor()方法創建的;

線程池的主要優點有三個:
1、重用線程,提高性能。創建、銷燬線程都是很耗資源的;
2、控制線程數量,避免大量線程搶佔資源而導致的阻塞現象;
3、能夠對線程進行簡單的管理。比如定時執行、排隊執行等;

ThreadPoolExecutor

ThreadPoolExecutor的構造方法如下:

public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler) {
    if (corePoolSize < 0 ||
            maximumPoolSize <= 0 ||
            maximumPoolSize < corePoolSize ||
            keepAliveTime < 0)
        throw new IllegalArgumentException();
    if (workQueue == null || threadFactory == null || handler == null)
        throw new NullPointerException();
    this.corePoolSize = corePoolSize;
    this.maximumPoolSize = maximumPoolSize;
    this.workQueue = workQueue;
    this.keepAliveTime = unit.toNanos(keepAliveTime);
    this.threadFactory = threadFactory;
    this.handler = handler;
}

1、參數 int corePoolSize
線程池的核心線程數量。核心線程會一直存活,即使它們處於空閒狀態。當然,如果將allowCoreThreadTimeOut屬性設置爲true,當核心線程空閒時間超過一定時間(由keepAliveTime屬性指定)後,還是會終止;

2、參數 int maximumPoolSize
線程池所能容納的最大線程數。當活動線程數達到極限後,新加入的線程會被阻塞;

3、參數 long keepAliveTime
線程空閒超時時間。當線程空閒時間超過這個值後,就會被系統終止;

4、參數 TimeUnit unit
用於指定keepAliveTime的時間單位。這是一個枚舉值,常用的有毫秒MILLISECONDS、秒SECONDS、分鐘MINUTES;

5、參數 BlockingQueue workQueue
存儲任務的隊列,就是存儲了Runnable對象的集合。既然可以添加/存儲很多Runnable對象,爲什麼還說線程數量是指定的呢?因爲這個集合只是存儲了Runnable對象,並不會調用其start()方法啓動線程來執行任務,而是在指定的線程中調用Runnable對象的run()方法,執行run()方法裏面的邏輯而已。

6、參數 ThreadFactory threadFactory
線程工廠,爲線程池提供創建線程的功能。ThreadFactory是一個接口,它只聲明瞭一個方法Thread newThread(Runnable r);

另外,ThreadPoolExecutor中還有個RejectedExecutionHandler handler成員變量,通過setRejectedExecutionHandler()方法設置。當線程池無法執行新任務時(比如線程已滿),會通過這個handler通知調用處。

線程池執行任務時,會遵循如下規則:
1、如果線程數未達到corePoolSize,當有新任務插入的時候,會啓動一個核心線程進行執行;
2、如果線程數已經達到corePoolSize,當有新任務插入時,會插入任務隊列中排隊等待;
3、如果任務隊列也滿了,就啓動一個非核心線程執行該任務;
4、如果線程數已經達到maximumPoolSize,就拒絕該任務,並通過RejectedExecutionHandler通知調用處;

Executors

一般情況下,我們很少會自己new ThreadPoolExecutor,而是使用Executors工具類來快速創建所需的線程池,我們看一下Executors的幾個常用方法:

1、固定線程數線程池

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

可以看出,這個線程池核心線程數和最大線程數一樣,所以這個線程池只有核心線程數,並且沒有超時時間,即使線程空閒,也不會終止。由於這些線程都不會被回收,所以這樣的線程池能快速響應外界請求。

2、單線程線程池

public static ExecutorService newSingleThreadExecutor() {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>()));
    }

從構造方法的參數可以看出,這是一個單線程的線程池,裏面只有一個核心線程。它的意義在於,統一在一個線程中處理所有任務,不需要考慮線程間的同步問題。

3、緩存線程池

public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());
    }

這個線程池沒有核心線程,全部是非核心線程,最大線程數是0x7fffffff,幾乎等於無限,並且設置了60秒的超時時間。這個線程池的任務隊列爲SynchronousQueue,這個比較特殊,可以理解爲它是一個無法存儲元素的隊列,也就是說這個線程池沒有任務隊列,即所有的新任務都會立即執行,要麼啓動新線程,要麼使用空閒線程。這種線程池比較適合量大、耗時少的任務,當所有任務執行完,超過60秒就回收,幾乎不佔用系統資源。

4、定時線程池

public ScheduledThreadPoolExecutor(int corePoolSize,
                                       ThreadFactory threadFactory) {
        super(corePoolSize, Integer.MAX_VALUE,
              DEFAULT_KEEPALIVE_MILLIS, MILLISECONDS,
              new DelayedWorkQueue(), threadFactory);
    }

這個線程池的核心線程固定,非核心線程無限大,超時時間DEFAULT_KEEPALIVE_MILLIS爲10毫秒,也就是空閒非核心線程立即回收。任務隊列是延時任務隊列,這類線程池主要是用來執行延時任務和重複任務的。

總結

一般情況下我們很少會自己來new這些線程池,但是SDK中很多地方都用到了線程池,上一篇研究了Handler,今天研究了線程池,學會了這兩個工具,我們就可以定製出更高效、更簡潔的異步任務處理方法了。

下一篇就以AsynTask源碼爲例子,來研究一下Handler和線程池在其中的運用。

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