1. 程式人生 > >《Android開發藝術探索》第11章 Android的線程和線程池

《Android開發藝術探索》第11章 Android的線程和線程池

ctas serial 主線程 message minute 方法 同時 pre 控制線

第11章 Android的線程和線程池

11.1 主線程和子線程

(1)在Java中默認情況下一個進程只有一個線程,也就是主線程,其他線程都是子線程,也叫工作線程。Android中的主線程主要處理和界面相關的事情,而子線程則往往用於執行耗時操作。線程的創建和銷毀的開銷較大,所以如果一個進程要頻繁地創建和銷毀線程的話,都會采用線程池的方式。
(2)在Android中除了Thread,還有HandlerThreadAsyncTask以及IntentService等也都扮演著線程的角色,只是它們具有不同的特性和使用場景。AsyncTask封裝了線程池和Handler,它主要是為了方便開發者在子線程中更新UI。HandlerThread是一種具有消息循環的線程,在它的內部可以使用Handler。IntentService是一個服務,它內部采用HandlerThread來執行任務,當任務執行完畢後就會自動退出。因為它是服務的緣故,所以和後臺線程相比,它比較不容易被系統殺死。
(3)從Android 3.0開始,系統要求網絡訪問必須在子線程中進行,否則網絡訪問將會失敗並拋出NetworkOnMainThreadException

這個異常,這樣做是為了避免主線程由於被耗時操作所阻塞從而出現ANR現象。
(4)AsyncTask是一個抽象泛型類,它提供了ParamsProgressResult三個泛型參數,如果task確實不需要傳遞具體的參數,那麽都可以設置為Void。下面是它的四個核心方法,其中doInBackground不是在主線程執行的。
onPreExecutedoInBackgroundonProgressUpdateonPostResult

11.2 Android中的線程形態

(1)AsyncTask的使用過程中的條件限制:
1.AsyncTask的類必須在主線程中加載,這個過程在Android 4.1及以上版本中已經被系統自動完成。
2.AsyncTask對象必須在主線程中創建,execute

方法必須在UI線程中調用。
3.一個AsyncTask對象只能執行一次,即只能調用一次execute方法,否則會報運行時異常。
4.在Android 1.6之前,AsyncTask是串行執行任務的,Android 1.6的時候AsyncTask開始采用線程池並行處理任務,但是從Android 3.0開始,為了避免AsyncTask帶來的並發錯誤,AsyncTask又采用一個線程來串行執行任務。盡管如此,在Android 3.0以及後續版本中,我們可以使用AsyncTask的executeOnExecutor方法來並行執行任務。但是這個方法是Android 3.0新添加的方法,並不能在低版本上使用。
(2)AsyncTask的原理
1.AsyncTask中有兩個線程池:SerialExecutor
THREAD_POOL_EXECUTOR。前者是用於任務的排隊,默認是串行的線程池;後者用於真正執行任務。AsyncTask中還有一個Handler,即InternalHandler,用於將執行環境從線程池切換到主線程。AsyncTask內部就是通過InternalHandler來發送任務執行的進度以及執行結束等消息。
2.AsyncTask排隊執行過程:系統先把參數Params封裝為FutureTask對象,它相當於Runnable;接著將FutureTask交給SerialExecutor的execute方法,它先把FutureTask插入到任務隊列tasks中,如果這個時候沒有正在活動的AsyncTask任務,那麽就會執行下一個AsyncTask任務,同時當一個AsyncTask任務執行完畢之後,AsyncTask會繼續執行其他任務直到所有任務都被執行為止。
(3)HandlerThread就是一種可以使用Handler的Thread,它的實現就是在run方法中通過Looper.prepare()來創建消息隊列,並通過Looper.loop()來開啟消息循環,這樣在實際的使用中就允許在HandlerThread中創建Handler了,外界可以通過Handler的消息方式通知HandlerThread執行一個具體的任務。HandlerThread的run方法是一個無限循環,因此當明確不需要再使用HandlerThread的時候,可以通過它的quit或者quitSafely方法來終止線程的執行。HandlerThread的最主要的應用場景就是用在IntentService中。
(4)IntentService是一個繼承自Service的抽象類,要使用它就要創建它的子類。IntentService適合執行一些高優先級的後臺任務,這樣不容易被系統殺死。IntentService的onCreate方法中會創建HandlerThread,並使用HandlerThread的Looper來構造一個Handler對象ServiceHandler,這樣通過ServiceHandler對象發送的消息最終都會在HandlerThread中執行。IntentService會將Intent封裝到Message中,通過ServiceHandler發送出去,在ServiceHandler的handleMessage方法中會調用IntentService的抽象方法onHandleIntent,所以IntentService的子類都要實現這個方法。

11.3 Android中的線程池

(1)使用線程池的好處:
1.重用線程,避免線程的創建和銷毀帶來的性能開銷;
2.能有效控制線程池的最大並發數,避免大量的線程之間因互相搶占系統資源而導致的阻塞現象;
3.能夠對線程進行簡單的管理,並提供定時執行以及指定間隔循環執行等功能。
(2)Executor只是一個接口,真正的線程池是ThreadPoolExecutor。ThreadPoolExecutor提供了一系列參數來配置線程池,通過不同的參數可以創建不同的線程池,Android的線程池都是通過Executors提供的工廠方法得到的。
(3)ThreadPoolExecutor的構造參數
1.corePoolSize:核心線程數,默認情況下,核心線程會在線程池中一直存活;
2.maximumPoolSize:最大線程數,當活動線程數達到這個數值後,後續的任務將會被阻塞;
3.keepAliveTime:非核心線程閑置時的超時時長,超過這個時長,閑置的非核心線程就會被回收;
4.unit:用於指定keepAliveTime參數的時間單位,有TimeUnit.MILLISECONDSTimeUnit.SECONDSTimeUnit.MINUTES等;
5.workQueue:任務隊列,通過線程池的execute方法提交的Runnable對象會存儲在這個參數中;
6.threadFactory:線程工廠,為線程池提供創建新線程的功能。它是一個接口,它只有一個方法Thread newThread(Runnable r)
7.RejectedExecutionHandler:當線程池無法執行新任務時,可能是由於任務隊列已滿或者是無法成功執行任務,這個時候就會調用這個Handler的rejectedExecution方法來通知調用者,默認情況下,rejectedExecution會直接拋出一個rejectedExecutionException
(4)ThreadPoolExecutor執行任務的規則:
1.如果線程池中的線程數未達到核心線程的數量,那麽會直接啟動一個核心線程來執行任務;
2.如果線程池中的線程數量已經達到或者超過核心線程的數量,那麽任務會被插入到任務隊列中排隊等待執行;
3.如果在步驟2中無法將任務插入到的任務隊列中,可能是任務隊列已滿,這個時候如果線程數量沒有達到規定的最大值,那麽會立刻啟動非核心線程來執行這個任務;
4.如果步驟3中線程數量已經達到線程池規定的最大值,那麽就拒絕執行此任務,ThreadPoolExecutor會調用RejectedExecutionHandlerrejectedExecution方法來通知調用者。
(5)AsyncTask的THREAD_POOL_EXECUTOR線程池的配置:
1.corePoolSize=CPU核心數+1;
2.maximumPoolSize=2倍的CPU核心數+1;
3.核心線程無超時機制,非核心線程在閑置時間的超時時間為1s
4.任務隊列的容量為128
(6)Android中常見的4類具有不同功能特性的線程池:
1.FixedThreadPool:線程數量固定的線程池,它只有核心線程;
2.CachedThreadPool:線程數量不固定的線程池,它只有非核心線程;
3.ScheduledThreadPool:核心線程數量固定,非核心線程數量沒有限制的線程池,主要用於執行定時任務和具有固定周期的任務;
4.SingleThreadPool:只有一個核心線程的線程池,確保了所有的任務都在同一個線程中按順序執行。

《Android開發藝術探索》第11章 Android的線程和線程池