十一、Android的線程和線程池

除了Thread本身以外,在Android中可以扮演線程的角色還有很多,比如AsyncTask和IntentService,同時HandlerThread也是一種特殊的線程。儘管AsyncTask,IntentService以及HandlerThread的表現形式都有別於傳統的線程,但是它們的本質任然是傳統的線程。
對於AsyncTask來說,它的底層用到線程池,對於IntentService和HandlerThread來說,它們的底層則直接使用了線程。

不同形式的線程雖然都是線程,但是具有不同的特性和使用場景。AsyncTask封裝了線程池和Handler,它主要是爲了方便開發者在子線程中更新UI。

HandlerThread是一種具有消息循環的線程,在它的內部可以使用Handler。

IntentService是一個服務,系統對其進行了封裝使其可以方便地執行後臺任務,IntentService內部採用HandlerThread來執行任務,當任務執行完成之後,IntentService會自動退出。

1.主線程和子線程

默認只有一個線程,就是主線程,主要處理和界面交互相關的邏輯,子線程也叫工作線程,除了主線程以外的線程都是子線程。

2.Android中的線程形態

分別介紹AsyncTask,HandlerThread以及IntentService。

2.1.AsyncTask

AsyncTask是一種輕量級的異步任務類,它可以在線程池中,執行後臺任務,然後把執行的進度和最終的結果傳遞給主線程,並在主線程中更新UI。從實現上面來說,AsyncTask封裝了Thread和Handler,通過AsyncTask可以更加方便地執行後臺任務以及在主線程中訪問UI,但是AsyncTask並不適合進行特別耗時的後臺任務,對於特別耗時的任務來說,建議使用線程池。

AsyncTask是一個抽象的泛型類,提供了Params(參數類型),Progress(進度類型)和Result(後臺任務返回的結果類型)這三個泛型參數。

AsyncTask提供了四個核心方法:
(1)onPreExecute(),在主線程中執行,在異步任務執行之前,此方法會被調用,一般可以用於做一些準備工作。
(2)doInBackground(Params… params),在線程池中執行,此方法用於執行異步任務,params參數表示異步任務的輸入參數。在此方法中可以通過publishProgress方法來更新任務的進度,publishProgress方法會調用onProgressUpdate方法,另外此方法需要返回計算結果給onPostExecute方法。
(3)onProgressUpdate(Progress…values),在主線程中執行,當後臺任務的執行進度發生變化時此方法會被調用。
(4)onPostExecute(Result result)在主線程中執行,在異步任務執行之後,此方法會被調用,其中result是後臺任務的返回值,即doInBackground的返回值。
注意:
(1)AsyncTask的類必須在主線程中加載,這就意味着第一次訪問AsyncTask必須發生在主線程,當然這個過程在Android 4.1以及以上的版本以及被系統自動完成。
(2)AsyncTask的對象必須在主線程中創建。
(3)execute方法必須在UI線程中調用。
(4)不要在程序中直接調用onPretExecute,onPostExecute,doInBackground和onProgressUpdate方法。
(5)一個AsyncTask對象只能執行一次,即只能調用一次execute方法,否則會報運行時異常,就是說執行同樣的任務,每次還是需要new對象。
(6)在Android 1.6之前,AsyncTask是串行執行任務的,Android 1.6的時候AsyncTask開始採用線程池處理並行任務,但是從Android 3.0開始,爲了避免AsyncTask所帶來併發錯誤,AsyncTask又採用一個線程來串行執行任務。儘管如此,在Android 3.0以後,我們任然可以通過AsyncTask的executeOnExec方utor法來並行地執行任務。

2.2.AsyncTask的工作原理

2.3.HandlerThread

HandlerThread繼承了Thread,它是一種可以使用Handler的Thread,它的實現也很簡單,就是在run方法中通過Looper.prepare()來創建消息隊列,並通過Looper.loop()來開啓消息循環,這樣在實際的使用中就允許在HandlerThread中創建Handler了。

2.4.IntentService

繼承了Service並且是一個抽象類,可以用於執行耗時任務,當任務執行後會自動停止。

3.Android中的線程池

線程池的優點:
(1)重用線程池中的線程,避免因爲線程的創建和銷燬所帶來的性能開銷;
(2)能有效控制線程池的最大併發數,避免大量線程之間因互相搶佔系統資源而導致的阻塞現象;
(3)能夠對線程進行簡單的管理,並提供定時執行以及指定間隔循環執行等功能;

3.1.ThreadPoolExecutor

ThreadPoolExecutor是線程池的真正實現,它的構造方法提供了一系列參數來配置線程池。
下面是常用的ThreadPoolExecutor的一個構造方法:

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

corePoolSize:線程池的核心線程數,核心線程會在線程池中一直存活,即使它們處於閒置狀態。
如果將ThreadPoolExecutor的allowCoreThreadTimeOut屬性設置爲true,那麼閒置的核心線程在等待新任務到來時會有超市策略,這個時間由keepAliveTime所指定,當等待時間超過keepAliveTime所指定的時長後,核心線程就會被終止。

maximumPoolSize:線程池所能容納的最大線程數,當活動線程數達到這個數值後,後續新任務將會阻塞。

keepAliveTime:非核心線程閒置時的超時時長,超過這個時長,非核心線程就會被回收。當ThreadPoolExecutor的allowCoreThreadTimeOut屬性爲true時,keepAliveTime同樣會作用於核心線程。

unit:用於指定keepAliveTime參數的時間單位,這是一個枚舉,常用的有TimeUnit.MILLISECONDS(毫秒),TimeUnit.SECONDS(秒),以及TimeUnit.MINUTES(分鐘)等。

workQueue:線程池中的任務隊列,通過線程池的execute方法提交的Runnable對象會存儲在這個參數中。

threadFactory:線程工廠,爲線程池提供新建線程的功能,ThreadFactory是一個接口,只有一個方法,Thread newThread(Runnable r)。

除了上面的主要參數外,ThreadPoolExecutor還有一個不常用的參數RejectedExecutionHandler。當線程池無法執行新任務時,這可能是由於任務隊列已滿或者是無法成功執行任務,這個時候ThreadPoolExecutor會調用handler的rejectedException方法來通知調用者,默認情況下,rejectedException會直接拋出一個RejectedExcecutionException。

ThreadPoolExecutor爲RejectedExecutionHandler提供了幾個可選值:
CallerRunsPolicy,AbortPolicy,DiscardPolicy,DiscardOldstPolicy,其中AbortPolicy是默認值,會直接拋出RejectedExcecutionException。

ThreadPoolExecutor執行任務大致遵循下面的規則:
(1)如果線程池中的線程數量未達到核心線程的數量,那麼會直接啓動一個核心線程來執行任務;
(2)如果線程池中的線程數量已經達到或者超過核心線程的數量,那麼任務會被插入到任務隊列中排隊等待執行;
(3)如果在第二步中無法將任務插入到任務隊列中,這往往是由於任務隊列已滿,這個時候如果線程數量未達到線程池規定的最大值,那麼會立刻啓動一個非核心線程來執行任務;
(4)如果第三步中線程數量已經達到線程池規定的最大值,那麼就拒絕執行此任務,ThreadPoolExecutor會調用RejectedExecutionHandler的rejectedExecution方法來通知調用者;

3.2.線程池的分類

介紹四類具有不同功能特性的線程池。

3.2.1.FixedThreadPool

通過Executors的newFixedThreadPool是一種線程數量固定的線程池,當線程處於空閒狀態時,它們並不會回收,除非線程池關閉了。
當所有線程都處於活動狀態時,新任務都會處於等待狀態,直到有線程空閒出來。

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

3.2.2.CachedThreadPool

通過Executors的newCachedThreadPool方法來建立。它是一種線程數量不定的線程池,它只有非核心線程,並且其最大線程數量未Integer.MAX_VALUE。
從CachedThreadPool的特性來看,這類線程池比較適合執行大量的耗時較少的任務。當整個線程池都處於閒置狀態時,線程池中的線程都會超時而停止,這個時候CachedThreadPool之中實際上是沒有任何線程的,它幾乎是不佔用任何系統資源的。

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

3.2.3.ScheduledThreadPool

通過Executors的newScheduledThreadPool方法來創建。它的核心線程數量是固定的,而非核心線程數是沒有限制的,並且當非核心線程閒置時會被立即回收。主要用於執行定時任務和具有固定週期的重複任務。

    public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
        return new ScheduledThreadPoolExecutor(corePoolSize);
    }
    public ScheduledThreadPoolExecutor(int corePoolSize) {
        super(corePoolSize, Integer.MAX_VALUE,
              DEFAULT_KEEPALIVE_MILLIS, MILLISECONDS,
              new DelayedWorkQueue());
    }

3.2.4.SingleThreadPool

通過Executors的newSingleThreadExecutors方法來創建。只有一個核心線程,確保所有的任務都在同一個線程中按順序執行。意義在於統一所有的外界任務到一個線程中,這使得在這些任務之間不需要處理線程同步的問題。

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

使用:

        Runnable command = new Runnable() {
            @Override
            public void run() {
                SystemClock.sleep(2000);
            }
        };
        ExecutorService fixedThreadPool = Executors.newFixedThreadPool(3);
        fixedThreadPool.execute(command);
        ExecutorService cachedThreadPool = Executors.newFixedThreadPool(3);
        cachedThreadPool.execute(command);
        ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(3);
        // 延遲10ms,每隔1000ms執行一次
        scheduledThreadPool.scheduleWithFixedDelay(command, 10, 1000, TimeUnit.MILLISECONDS);
        ExecutorService singleThreadPool = Executors.newSingleThreadExecutor();
        singleThreadPool.execute(command);
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章