從用途上來說,線程分為主線程和子線程,主線程主要處理和界面相關的事情,子線程則往往用于執行耗時操作。
除了Thread本身以外,在Android中可以扮演線程角色的還有很多:AsyncTak、IntentService和HandlerThread。
AsyncTask:底層用到了線程池;封裝了線程池和Handler,主要是為了方便開發者在子線程中更新UI
IntentService:底層直接使用線程,它是一個服務,系統對其進行了封裝使其可以更方便地執行后臺任務。內部采用了HandlerThread來執行任務,當任務執行完畢后IntentService會自動退出。從任務執行的角度來看,IntentService的作用很像一個后臺線程,但是IntentService是一種服務,他不容易被系統殺死從而可以盡量保證任務的執行,而如果是一個后臺線程,由于這個時候線程中沒有活動的四大組件,那么這個進程的優先級就會非常低,會很容易被系統殺死。
HandlerThread:底層直接使用線程,是一種具有消息循環的線程,在它的內部可以使用Handler。
一、主線程和子線程
主線程是指進程所擁有的的線程,在Java默認情況下一個進程只有一個主線程。主線程的作用是運行四大組件以及處理它們和用戶的交互,而子線程的作用則是執行耗時任務,比如網絡請求、I/O操作等。
二、Android中的線程形態
2.1 AsyncTask
是一種輕量級的異步任務類,它可以在線程池中執行后臺任務,然后把執行的進度和最終結果傳遞給主線程并在主線程中更新UI。但AsyncTask并不適合特別耗時的后臺任務,對于特別耗時的任務來說,建議使用線程池。
AsyncTask是一個抽象的泛型類,它提供了Params、Progress和Result這三個泛型參數,其中,Params表示參數的類型,Progress表示后臺任務的執行進度的類型,而Result則表示后臺任務的返回結果的類型。這個類的聲明如下:
public abstract class AsyncTask<Params,Progress,Result>。
提供了四個核心的方法:
1、onPreExcute(),在線程中執行,在異步任務執行之前,吃方法會被調用,一般可以用于做一些準備工作
2、doInBackground(Params...params),在線程池中執行,此方法用于執行異步任務,params參數表示異步任務的輸入參數。在此方法中可通過publishProgress方法來更新任務的進度,publishProgress方法會調用onProgressUpdate方法。另外此方法需要返回計算結果給onPostExcute方法。
3、onProgressUpdate(Progress...values),在主線初中執行,當后臺任務的執行進度發生時此方法會被調用
4、onPostExcute(Result result),在主線程中執行,在異步任務執行之后,此方法會被調用,其中result參數是后臺任務的返回值,即doInBackground的返回值
AsyncTask還提供了onCancelled()方法,它同樣在主線程中執行,當異步任務唄取消時,onCancelled()方法會被調用,這個時候onPostExcute則不會被調用。
2.2 AsyncTask的工作原理
從它的execute方法開始分析:
sDefaultExecutor實際上是一個串行的線程池,一個進程中所有的AsyncTask全部在這個串行的線程池中排隊執行。
execute方法又調用了executeOnExecutor方法:
在此方法中,onPreExecute方法最先執行,然后線程池開始執行。
下面分析線程池的執行過程:
從SerialExecutor的實現可以分析AsyncTask的排隊執行過程。
首先系統會把AsyncTask的Params參數封裝為FutureTask對象,FutureTask是一個并發類,在這里它充當了Runnable的作用。
接著這個FutureTask會交給SerialExecutor的execute方法處理,SerialExecutor的execute方法首先會把FutureTask對象插入到任務隊列mTasks中,如果這個時候沒有正在活動的AsyncTask任務,那么就會調用SerialExecutor的scheduleNext方法來執行下一個AsyncTask任務,同時當一個AsyncTask任務執行完后,AsyncTask會繼續執行其他任務直到所有的任務都被執行為止。
AsyncTask中有兩個線程池(SerialExcutor和THREAD_POOL_EXECUTOR)和一個Handler(InternalHandler),其中線程池SerialExecutor用于任務的排隊,而線程池THREAD_POOL_EXECUTOR用于真正地執行任務,InternalHandler用于將執行環境從線程池切換到主線程,其本質仍然是線程的調用過程。
由于FutureTask的run方法會調用mWorker的call方法,因此mWorker的call方法最終會在線程池中執行:
在mWorker的call方法中,首先將mTaskInvoked設為true,表示當前任務已經被調用過了,然后執行AsyncTask的doInBAckground方法,接著將其返回值傳遞給postResult方法:
postResult方法會通過getHandler發送一個MESSAGE_POST_RESULT的消息,其定義如下:
2.3 HandlerThread
HandlerThread繼承了Thread,是一種可以使用Handler的Thread,它的實現是在run方法中通過Looper.prepare來創建消息隊列,并通過Looper.prepare來開啟消息循環,這樣在實際的使用中就允許在HandlerThread中創建Handler了,其run方法如下:
從HandlerThread的實現來看,普通Thread主要用于在run方法中執行一個耗時任務,而HandlerThread在內部創建了消息隊列,外界需要通過Handler的消息方式來通知HandlerThread執行一個具體的任務,其使用場景是在IntentService中。
由于HandlerThread的run方法是一個無限循環,因此當明確不需要再使用HandlerThread時,可通過它的quit或者quitSafely方法來終止線程的執行。
2.4 IntentService
IntentService是一種特殊的Service,它繼承了Service并且它是一個抽象類,因此必須創建它的子類側能使用IntentService。
IntentService可用于執行后臺耗時的任務,當任務執行后它會自動停止,同時由于IntentService是服務的原因,這導致它的優先級比單純的線程要高很多,所以IntentService比較適合執行一些高優先級的后臺任務,因為它優先級高不容易被系統殺死。
在實現上,IntentService封裝了HandlerThread和Handler,這一點可從其onCreate方法中看出:
當IntentService被第一次創建時,其onCreate方法會被調用,onCreate方法會創建一個HandlerThread,然后使用它的Looper來構造一個Handler對象mServiceHandler,這樣通過mServiceHandler發送的消息最終都會在HandlerThread中執行,從這個角度來看,IntentService也可以用于執行后臺任務。每次啟動IntentService,它的onStartCommand方法就會被調用一次,IntentService在onStartCommand中處理每個后臺任務的Intent,而onStartCommand調用了onStart:
可以看出,IntentService僅僅是通過mServiceHandler發送了一個消息,這個消息會在HandlerThread中被處理。
mServiceHandler收到消息后,會將intent對象傳遞給onHandleIntent方法處理。通過這個Intent對象即可解析出外界啟動IntentService時所傳遞的參數,通過這些參數就可以區分具體的后臺任務,這樣在onHandlerIntent方法中就可以對不同的后臺任務做處理了。當onHandleIntent方法執行結束后,IntentService就會通過stopSelf(int startId)方法來嘗試停止服務,stopSelf(int startId)會等待所有的消息都處理完畢后才停止服務,而stopSelf()則直接終止服務。
IntentService的onHandleIntent方法是一個抽象方法,它需要我們在子類中實現,它的作用是從Intent參數中區分具體的任務并執行這些任務。如果目前只存在一個后臺任務,那么onHandleIntent方法執行完這個任務后,stopSelf(int startId)就會直接停止服務,如果目前存在多個后臺任務,那么當onHandleIntent方法執行完最后一個任務時,stopSelf(int startId)才會終止服務。
三、Android中的線程池
線程池優點:
1、重用線程池中的線程,避免因為線程的創建和銷毀所帶來的性能開銷
2、能有效控制線程池的最大開發并發數,避免大量的線程之間因相互搶占系統資源而導致的阻塞現象
3、能夠對線程進行簡單的管理,并提供定時執行以及指定間隔執行等功能
3.1 ThreadPoolExecutor
ThreadPoolExecutor是線程池的真正實現,它的構造方法提供了一系列參數來配置線程池,下面是ThreadPoolExecutor的一個比較常用的構造方法:
publicThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue workQueue,ThreadFactory threadFactory)
corePoolSize
線程池的核心線程數,默認情況下,核心線程會在線程池中一直存活,即使它們處于閑置狀態。如果將ThreadPoolExecutor的allowCoreThreadTimeOut屬性設置為true,那么閑置的核心線程在等待新任務到來時會有超時策略,這個時間間隔由keepAliveTime所指定,當等待時間超過keepAliveTime所指定的時長后,核心線程就會被終止。
maximumPoolSize
線程池所能容納的最大線程數,當活動線程數達到這個數值后,后續的新任務將會被阻塞
keepAliveTime
非核心線程閑置時的超時時長,超過這個時長,非核心線程就會被回收。當ThreadPoolExecutor的allowCoreThreadTimeOut屬性設置為true時,keepAliveTime同樣會作用于核心線程。
unit
用于指定keepAliveTime參數的時間單位,這是一個枚舉,常用的有TimeUnit.MILLISECONDS(毫秒)、TimeUnit.SECONDS(秒)以及TimeUnit.MINUTEX(分鐘)等。
workQueue
線程池中的任務隊列,通過線程池的execute方法日膠的Runnable對象會存儲在這個參數中。
threadFactory
線程工廠,為線程池提供創建新線程的功能。ThreadFactory是一個接口,只有一個方法:Thread newThread(Runnable r)。
ThreadPoolExecutor執行任務時大致遵循如下規則:
1、如果線程池中的線程數量未必達到核心線程的數量,那么會直接啟動一個核心線程來執行任務
2、如果線程池中的線程數量已經達到或者超過核心線程的數量,那么任務會被插入到任務隊列中排隊等到執行
3、如果在步驟二中無法將任務插入到任務隊列中,這往往是由于任務隊列已滿,這個時候如果線程數量未達到線程池規定的最大值,那么會立刻啟動一個非核心線程來執行任務。
4、如果步驟3中線程數量已經達到線程池規定的最大值,那么就拒絕執行此任務,ThreadPoolExecutor會調用RejectedExecutionHandler的rejectedExecution方法來通知調用者。
ThreadPoolExecutor的參數配置在AsyncTask中有明顯的提現,下面是AsyncTask中線程池的配置情況:
從上面的代碼可以知道,AsyncTask對THREAD_POOL_EXECUTOR這個線程池進行了配置,其規格是:
核心線程數等于CPU核心數+1
線程池的最大線程數為CPU核心數的2倍+1
核心線程無超時機制,非核心線程在閑置時的超時時間為1秒
任務隊列容量為128
3.2 線程池的分類
3.2.1 FixedThreadPool
通過Executors的newFixedThreadPool方法來創建。它是一種線程數量固定的線程池,當線程處于空閑狀態時,它們并不會被回收,除非線程池被關閉了。當所有的線程都處于活動狀態時,新任務都會處于等待狀態,直到有線程空閑出來。由于newFixedThreadPool只有核心線程并且這些核心線程不會被回收,這意味著它能夠更快的響應外界的請求:
3.2.2 CachedThreadPool
通過Executors的newCachedThreadPool方法來創建。是一種線程數量不定的線程池,只有非核心線程,并且最大線程數為Interger.MAX_VALUE。當線程池中的線程都處于活動狀態時,線程池會創建新的線程來處理新任務,否則就會利用空閑的線程來處理新任務。
線程池中的空閑線程都有超時限制,這個超時時長為60秒。超過60秒的閑置線程就會被回收。
CachedThreadPool的任務隊列相當于一個空集合,這將導致任何任務都會立即被執行,因為在這種場景下SynchronousQueue是無法插入任務的。
這類線程池比較適合執行大量的耗時比較少的任務。
2.2.3 ScheduledThreadPool
通過Executors的newScheduledThreadPool方法來創建。核心線程數量是固定的,而非核心線程是沒有限制的,并且當非核心線程閑置時會被立即回收。
ScheduledThreadPool主要用于執行定時任務和具有固定周期的重復任務
2.2.4 ?SingleThreadExecutor
通過Executors的newSingleThreadExecutor方法來創建。這類線程池內部只有一個核心線程,它確保所有的任務都在同一個線程中按順序執行。其意義在于統一所有的外界任務到一個線程中,這使得在這些任務之間不需要處理線程同步的問題。