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