前言
在Android中,線程的形態(tài)有很多種:
- AsyncTask封裝了線程池和Handler,主要為了方便開發(fā)者在子線程中更新UI,底層是線程池。
- HandlerThread是具有消息循環(huán)的線程,內(nèi)部可以使用handler,底層是Thread。
- IntentService是一種Service,內(nèi)部采用HandlerThread來執(zhí)行任務(wù),當任務(wù)執(zhí)行完畢后IntentService會自動退出。由于它是一種Service,所以不容易被系統(tǒng)殺死,底層是Thread 。
操作系統(tǒng)中,線程是操作系統(tǒng)調(diào)度的最小單元,同時線程又是一種受限的系統(tǒng)資源(不可能無限產(chǎn)生),其創(chuàng)建和銷毀都會有相應(yīng)的開銷。同時當系統(tǒng)存在大量線程時,系統(tǒng)會通過時間片輪轉(zhuǎn)的方式調(diào)度每個線程,因此線程不可能做到絕對的并發(fā),除非線程數(shù)量小于等于CPU的核心數(shù)。頻繁創(chuàng)建銷毀線程不明智,使用線程池是正確的做法。線程池會緩存一定數(shù)量的線程,通過線程池就可以避免因為頻繁創(chuàng)建和銷毀線程所帶來的系統(tǒng)開銷。
一、主線程和子線程
主線程也叫UI線程,作用是運行四大組件以及處理它們和用戶交互。子線程的作用是執(zhí)行耗時操作,比如I/O,網(wǎng)絡(luò)請求等。從Android 3.0開始,主線程中訪問網(wǎng)絡(luò)將拋出異常。
二、Android中的線程形態(tài)
1、AsyncTask
AsyncTask是一種輕量級的異步任務(wù)類,封裝了Thread和Handler,可以在線程池中執(zhí)行后臺任務(wù),然后把執(zhí)行的進度和最終的結(jié)果傳遞給主線程并更新UI。但并不適合進行特別耗時的后臺任務(wù),對于特別耗時的任務(wù)來說, 建議使用線程池。
abstract class AsyncTask<Params, Progress, Result>
- Params:入?yún)㈩愋?/li>
- Progress:后臺任務(wù)的執(zhí)行進度的類型
- Result:后臺任務(wù)的返回結(jié)果的類型
如果不需要傳遞具體的參數(shù), 那么這三個泛型參數(shù)可以用Void來代替。
1.1、四個核心方法
-
onPreExecute() void
在主線程執(zhí)行, 在異步任務(wù)執(zhí)行之前, 此方法會被調(diào)用, 一般可以用于做一些準備工作。 -
doInBackground(Params... params) Result
在線程池中執(zhí)行, 此方法用于執(zhí)行異步任務(wù), 參數(shù)params表示異步任務(wù)的輸入?yún)?shù)。 在此方法中可以通過publishProgress(Progress... values) void
方法來更新任務(wù)的進度,publishProgress()
方法會調(diào)用onProgressUpdate()
方法。另外此方法需要返回計算結(jié)果給onPostExecute()
-
onProgressUpdate(Progress... values) void
在主線程執(zhí)行,當后臺任務(wù)publishProgress()
時,會被調(diào)用。 -
onPostExecute(Result res) void
在主線程執(zhí)行, 在異步任務(wù)執(zhí)行之后, 此方法會被調(diào)用, 其中result參數(shù)是后臺任務(wù)的返回值, 即doInBackground的返回值。
除了上述的四種方法,還有onCancelled()
, 它同樣在主線程執(zhí)行, 當異步任務(wù)被取消時調(diào)用,這個時候onPostExecute()則不會被調(diào)用.
1.2、AsyncTask使用過程中的一些條件限制
- AsyncTask的類必須在主線程被加載, 這就意味著第一次訪問AsyncTask必須發(fā)生在主線程。在Android 4.1及以上的版本已經(jīng)被系統(tǒng)自動完成。
- AsyncTask的對象必須在主線程中創(chuàng)建。
- execute方法必須在UI線程調(diào)用。
- 不要在程序中直接調(diào)用onPreExecute(), onPostExecute(), doInBackground和onProgressUpdate()
- 一個AsyncTask對象只能執(zhí)行一次, 即只能調(diào)用一次execute()方法, 否則會報運行時異常。
- AsyncTask采用了一個線程來串行的執(zhí)行任務(wù)。 盡管如此在3.0以后, 仍然可以通過
AsyncTask#executeOnExecutor()
方法來并行執(zhí)行任務(wù)。
1.3、AsyncTask的工作原理
AsyncTask中有兩個線程池(SerialExecutor和THREAD_POOL_EXECUTOR)和一個Handler(InternalHandler), 其中線程池SerialExecutor用于任務(wù)的排列, 而線程池THREAD_POOL_EXECUTOR用于真正的執(zhí)行任務(wù), 而InternalHandler用于將執(zhí)行環(huán)境從線程切換到主線程, 其本質(zhì)仍然是線程的調(diào)用過程。
AsyncTask的排隊過程:首先系統(tǒng)會把AsyncTask#Params參數(shù)封裝成FutureTask對象, FutureTask是一個并發(fā)類, 在這里充當了Runnable的作用. 接著這個FutureTask會交給SerialExecutor#execute()方法去處理. 這個方法首先會把FutureTask對象插入到任務(wù)隊列mTasks中, 如果這個時候沒有正在活動AsyncTask任務(wù), 那么就會調(diào)用SerialExecutor#scheduleNext()方法來執(zhí)行下一個AsyncTask任務(wù). 同時當一個AsyncTask任務(wù)執(zhí)行完后, AsyncTask會繼續(xù)執(zhí)行其他任務(wù)直到所有的任務(wù)都執(zhí)行完畢為止, 從這一點可以看出, 在默認情況下, AsyncTask是串行執(zhí)行的。
2、HandlerThread
HandlerThread繼承了Thread, 它是一種可以使用Handler的Thread, 它的實現(xiàn)也很簡單, 就是run方法中通過Looper.prepare()來創(chuàng)建消息隊列, 并通過Looper.loop()來開啟消息循環(huán), 這樣在實際的使用中就允許在HandlerThread中創(chuàng)建Handler.
從HandlerThread的實現(xiàn)來看, 它和普通的Thread有顯著的不同之處. 普通的Thread主要用于在run方法中執(zhí)行一個耗時任務(wù); 而HandlerThread在內(nèi)部創(chuàng)建了消息隊列, 外界需要通過Handler的消息方式來通知HandlerThread執(zhí)行一個具體的任務(wù). HandlerThread是一個很有用的類, 在Android中一個具體使用場景就是IntentService.
由于HandlerThread#run()是一個無線循環(huán)方法, 因此當明確不需要再使用HandlerThread時, 最好通過quit()或者quitSafely()方法來終止線程的執(zhí)行.
3、IntentService
IntentSercie是一種特殊的Service,繼承了Service并且是抽象類,任務(wù)執(zhí)行完成后會自動停止,優(yōu)先級遠高于普通線程,適合執(zhí)行一些高優(yōu)先級的后臺任務(wù); IntentService封裝了HandlerThread和Handler
onCreate方法自動創(chuàng)建一個HandlerThread,用它的Looper構(gòu)造了一個Handler對象mServiceHandler,這樣通過mServiceHandler發(fā)送的消息都會在HandlerThread執(zhí)行;IntentServiced的onHandlerIntent方法是一個抽象方法,需要在子類實現(xiàn),onHandlerIntent方法執(zhí)行后,stopSelt(int startId)就會停止服務(wù),如果存在多個后臺任務(wù),執(zhí)行完最后一個stopSelf(int startId)才會停止服務(wù)。
三、Android線程池
線程池優(yōu)點:
1) 重用線程池的線程,減少線程創(chuàng)建和銷毀帶來的性能開銷
2) 控制線程池的最大并發(fā)數(shù),避免大量線程互相搶系統(tǒng)資源導(dǎo)致阻塞
3) 提供定時執(zhí)行和間隔循環(huán)執(zhí)行功能
Android中的線程池的概念來源于Java中的Executor,Executor是一個接口, 真正的線程池的實現(xiàn)為ThreadPoolExecutor。Android的線程池大部分都是通過Executor提供的工廠方法創(chuàng)建的。 ThreadPoolExecutor提供了一系列參數(shù)來配制線程池,通過不同的參數(shù)可以創(chuàng)建不同的線程池。 而從功能的特性來分的話可以分成四類。
1、ThreadPoolExecutor
ThreadPoolExecutor是線程池的真正實現(xiàn), 它的構(gòu)造方法提供了一系列參數(shù)來配置線程池, 這些參數(shù)將會直接影響到線程池的功能特性。
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), defaultHandler);
}
-
corePoolSize
: 線程池的核心線程數(shù), 默認情況下, 核心線程會在線程池中一直存活, 即使都處于閑置狀態(tài). 如果將ThreadPoolExecutor#allowCoreThreadTimeOut
屬性設(shè)置為true, 那么閑置的核心線程在等待新任務(wù)到來時會有超時的策略, 這個時間間隔由keepAliveTime屬性來決定 當?shù)却龝r間超過了keepAliveTime設(shè)定的值那么核心線程將會終止 -
maximumPoolSize
: 線程池所能容納的最大線程數(shù), 當活動線程數(shù)達到這個數(shù)值之后, 后續(xù)的任務(wù)將會被阻塞 -
keepAliveTime
: 非核心線程閑置的超時時長, 超過這個時長, 非核心線程就會被回收 -
allowCoreThreadTimeOut
這個屬性為true的時候, 這個屬性同樣會作用于核心線程 -
unit
: 用于指定keepAliveTime參數(shù)的時間單位, 這是一個枚舉, 常用的有TimeUtil.MILLISECONDS(毫秒), TimeUtil.SECONDS(秒)以及TimeUtil.MINUTES(分) -
workQueue
: 線程池中的任務(wù)隊列, 通過線程池的execute方法提交的Runnable對象會存儲在這個參數(shù)中 -
threadFactory
: 線程工廠, 為線程池提供創(chuàng)建新線程的功能. ThreadFactory是一個接口
ThreadPoolExecutor執(zhí)行任務(wù)大致遵循如下規(guī)則:
如果線程池中的線程數(shù)量未達到核心線程的數(shù)量, 那么會直接啟動一個核心線程來執(zhí)行任務(wù).
如果線程池中的線程數(shù)量已經(jīng)達到或者超過核心線程的數(shù)量, 那么任務(wù)會被插入到任務(wù)隊列中排隊等待執(zhí)行.
如果在步驟2中無法將任務(wù)插入到任務(wù)隊列中, 這通常是因為任務(wù)隊列已滿, 這個時候如果線程數(shù)量未達到線程池的規(guī)定的最大值, 那么會立刻啟動一個非核心線程來執(zhí)行任務(wù).
如果步驟3中的線程數(shù)量已經(jīng)達到最大值的時候, 那么會拒絕執(zhí)行此任務(wù),ThreadPoolExecutor會調(diào)用RejectedExecution方法來通知調(diào)用者。
AsyncTask的THREAD_POOL_EXECUTOR線程池配置:
- 核心線程數(shù)等于CPU核心數(shù)+1
- 線程池最大線程數(shù)為CPU核心數(shù)的2倍+1
- 核心線程無超時機制,非核心線程的閑置超時時間為1秒
- 任務(wù)隊列容量是128
2、線程池的分類
FixedThreadPool
通過Executor#newFixedThreadPool()方法來創(chuàng)建. 它是一種線程數(shù)量固定的線程池, 當線程處于空閑狀態(tài)時, 它們并不會被回收, 除非線程池關(guān)閉了. 當所有的線程都處于活動狀態(tài)時, 新任務(wù)都會處于等待狀態(tài), 直到有線程空閑出來. 由于FixedThreadPool只有核心線程并且這些核心線程不會被回收, 這意味著它能夠更加快速地響應(yīng)外界的請求.CachedThreadPool
通過Executor#newCachedThreadPool()方法來創(chuàng)建. 它是一種線程數(shù)量不定的線程池, 它只有非核心線程, 并且其最大值線程數(shù)為Integer.MAX_VALUE. 這就可以認為這個最大線程數(shù)為任意大了. 當線程池中的線程都處于活動的時候, 線程池會創(chuàng)建新的線程來處理新任務(wù), 否則就會利用空閑的線程來處理新任務(wù). 線程池中的空閑線程都有超時機制, 這個超時時長為60S, 超過這個時間那么空閑線程就會被回收.
和FixedThreadPool不同的是, CachedThreadPool的任務(wù)隊列其實相當于一個空集合, 這將導(dǎo)致任何任務(wù)都會立即被執(zhí)行, 因為在這種場景下SynchronousQueue是無法插入任務(wù)的. SynchronousQueue是一個非常特殊的隊列, 在很多情況下可以把它簡單理解為一個無法存儲元素的隊列. 在實際使用中很少使用.這類線程比較適合執(zhí)行大量的耗時較少的任務(wù)ScheduledThreadPool
通過Executor#newScheduledThreadPool()方法來創(chuàng)建. 它的核心線程數(shù)量是固定的, 而非核心線程數(shù)是沒有限制的, 并且當非核心線程閑置時會立刻被回收掉. 這類線程池用于執(zhí)行定時任務(wù)和具有固定周期的重復(fù)任務(wù)SingleThreadExecutor
通過Executor#newSingleThreadPool()方法來創(chuàng)建. 這類線程池內(nèi)部只有一個核心線程, 它確保所有的任務(wù)都在同一個線程中按順序執(zhí)行. 這類線程池意義在于統(tǒng)一所有的外界任務(wù)到一個線程中, 這使得在這些任務(wù)之間不需要處理線程同步的問題