再梳理一篇多線程的文章,正好續(xù)上之前的多線程章節(jié),完善下這個(gè)章節(jié)內(nèi)容。
一、多線程方案:
ThreadPool: 把任務(wù)分解成不同的單元,分發(fā)到各個(gè)不同的線程上,進(jìn)行同時(shí)并發(fā)處理。
AsyncTask: 為UI線程與工作線程之間進(jìn)行快速的切換提供一種簡單便捷的機(jī)制。
HandlerThread: 為某些回調(diào)方法或者等待某些任務(wù)的執(zhí)行設(shè)置一個(gè)專屬的線程,并提供線程任務(wù)的調(diào)度機(jī)制。
IntentService: 適合于執(zhí)行由UI觸發(fā)的后臺Service任務(wù),并可以把后臺任務(wù)執(zhí)行的情況通過一定的機(jī)制反饋給UI。
二、逐一分析
2.1 ThreadPool
適用場景:任務(wù)量相對大,需要把任務(wù)進(jìn)行分解,并發(fā)進(jìn)行執(zhí)行的場景。
優(yōu)點(diǎn):線程池的優(yōu)點(diǎn)是相比較單獨(dú)創(chuàng)建Thread而言的。
- 線程復(fù)用減少開銷。
- 對線程統(tǒng)一管理,有效控制最大并發(fā)線程數(shù)。
- 提供更多功能,比如定時(shí)執(zhí)行、定期執(zhí)行、單線程等。
BlockingQueue阻塞隊(duì)列介紹:
阻塞隊(duì)列常用于生產(chǎn)者消費(fèi)者場景,常用的阻塞隊(duì)列如下:
-
ArrayBlockingQueue數(shù)組實(shí)現(xiàn)的有界阻塞隊(duì)列。 -
LinkedBlockingQueue鏈表實(shí)現(xiàn)的有界阻塞隊(duì)列。 -
DelayQueue使用優(yōu)先級隊(duì)列實(shí)現(xiàn)的無界阻塞隊(duì)列。 -
PriorityBlockingQueue支持優(yōu)先級排序的無界阻塞隊(duì)列。 -
SynchronousQueue不存儲元素的阻塞隊(duì)列,進(jìn)一個(gè)元素必先出一個(gè)。 -
LinkedTransferQueue鏈表實(shí)現(xiàn)的無界阻塞隊(duì)列。 -
LinkedBlockingDeque鏈表實(shí)現(xiàn)的雙向阻塞隊(duì)列。
最常用的ArrayBlockingQueue和LinkedBlockingQueue區(qū)別:
隊(duì)列中鎖的實(shí)現(xiàn)不同:
ArrayBlockingQueue實(shí)現(xiàn)的隊(duì)列中的鎖是沒有分離的,即生產(chǎn)和消費(fèi)用的是同一個(gè)鎖;
LinkedBlockingQueue實(shí)現(xiàn)的隊(duì)列中的鎖是分離的,即生產(chǎn)用的是putLock,消費(fèi)是takeLock
在生產(chǎn)或消費(fèi)時(shí)操作不同:
ArrayBlockingQueue實(shí)現(xiàn)的隊(duì)列中在生產(chǎn)和消費(fèi)的時(shí)候,是直接將枚舉對象插入或移除的;
LinkedBlockingQueue實(shí)現(xiàn)的隊(duì)列中在生產(chǎn)和消費(fèi)的時(shí)候,需要把枚舉對象轉(zhuǎn)換為Node進(jìn)行插入或移除,會影響性能
隊(duì)列大小初始化方式不同:
ArrayBlockingQueue實(shí)現(xiàn)的隊(duì)列中必須指定隊(duì)列的大??;
LinkedBlockingQueue實(shí)現(xiàn)的隊(duì)列中可以不指定隊(duì)列的大小,但是默認(rèn)是Integer.MAX_VALUE
好,簡單了解阻塞隊(duì)列之后,接下來了解線程池,先來看下線程池的處理流程:

線程池的分類介紹:
線程池就是通過設(shè)置ThreadPoolExecutor的不同參數(shù)來創(chuàng)建不同類型的線程池,那先來了解下參數(shù):
public ThreadPoolExecutor(int corePoolSize, //核心線程數(shù)
int maximumPoolSize,//線程池允許創(chuàng)建的最大線程數(shù)
long keepAliveTime,//非核心線程閑置的超時(shí)時(shí)間
TimeUnit unit,//keepAliveTime的時(shí)間單位
BlockingQueue<Runnable> workQueue)//任務(wù)隊(duì)列
常用的線程池4類:
FixedThreadPool:
public static ExecutorService newFixedThreadPool(int nThreads){
return new ThreadPoolExecutor(
nThreads, //核心線程固定
nThreads, // 最大線程 = 核心線程
0L, //那么自然也不需要非核心線程閑置的超時(shí)時(shí)間
TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());//無界阻塞隊(duì)列
}
總結(jié):FixedThreadPool就是固定核心線程的線程池,無非核心線程,并且核心線程不會被回收,當(dāng)執(zhí)行execute方法時(shí),未達(dá)到核心線程數(shù),直接取空閑線程用,達(dá)到核心線程數(shù)加入無界阻塞隊(duì)列等待空閑線程釋放。
適用場景:低并發(fā)但每個(gè)任務(wù)耗時(shí)較長的場景。

CacheThreadPool:
public static ExecutorService newCacheThreadPool(){
return new ThreadPoolExecutor(
0, //無核心線程
Integer.MAX_VALUE, //非核心線程無限制
60L,//非核心線程閑置的超時(shí)時(shí)間為60s
TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());//不存儲元素的阻塞隊(duì)列,出一個(gè)才能進(jìn)一個(gè)。
}
總結(jié):當(dāng)執(zhí)行execute方法時(shí),向SynchronousQueue提交任務(wù),此時(shí)SynchronousQueue需要移除一個(gè)任務(wù)去被執(zhí)行,如果此時(shí)有空閑線程則交給空閑線程處理,沒有則新建線程處理。空閑線程超過60s沒被使用則回收。
適用場景:高并發(fā)但每個(gè)任務(wù)耗時(shí)較少的場景。但是實(shí)際上一般都不會用,因?yàn)榉呛诵木€程無限制,如果一直沒有空閑線程,一直不停創(chuàng)建新的線程,很容易就OOM了。

ScheduledThreadPool
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
return new ScheduledThreadPoolExecutor(corePoolSize);
}
public ScheduledThreadPoolExecutor(int corePoolSize) {
super(corePoolSize,
Integer.MAX_VALUE,
DEFAULT_KEEPALIVE_MILLIS, //10L
MILLISECONDS,
new DelayedWorkQueue());
}
ScheduledThreadPool是一個(gè)能實(shí)現(xiàn)定時(shí)和周期性任務(wù)的線程池,從配置上看它是定時(shí)版的FixedThreadPool。
總結(jié):當(dāng)執(zhí)行ScheduledThreadPoolExecutor的scheduleAtFixedRate或scheduleWithFixedDelay方法,會向DelayedWorkQueue添加一個(gè)實(shí)現(xiàn)RunnableScheduledFuture接口的任務(wù)包裝類ScheduledFutureTask,并檢查運(yùn)行的線程是否達(dá)到核心線程數(shù)corePoolSize。如果沒有就新建線程,并啟動。但并非立即執(zhí)行任務(wù),而是去DelayedWorkQueue中取任務(wù)包裝類ScheduledFutureTask,然后再去執(zhí)行任務(wù);如果運(yùn)行的線程達(dá)到了corePoolSize,就把任務(wù)添加到任務(wù)隊(duì)列DelayedWorkQueue中;DelayedWorkQueue會將任務(wù)排序,先執(zhí)行的任務(wù)放在隊(duì)列的前面。任務(wù)執(zhí)行完后,ScheduledFutureTask中的變量time改為下次要執(zhí)行的時(shí)間,并放回到DelayedWorkQueue中。
適用場景:有周期性任務(wù)和定時(shí)需求。

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

總結(jié):執(zhí)行execute方法時(shí),若當(dāng)前運(yùn)行的線程數(shù)未達(dá)到核心線程數(shù)(沒有正在運(yùn)行的線程),就創(chuàng)建一個(gè)新線程來處理任務(wù);如果當(dāng)前有運(yùn)行的線程,就把任務(wù)添加到阻塞隊(duì)列LinkedBlockingQueue。SingleThreadPool能夠確保所有的任務(wù)都在一個(gè)線程中按照順序逐一執(zhí)行。
適用場景:需要任務(wù)在一個(gè)線程中按順序執(zhí)行。
2.2 AsyncTask
適用場景:任務(wù)盡量單一的且異步執(zhí)行的生命周期短暫的場景。
技術(shù)特點(diǎn):
- 線程池:
private static final int CORE_POOL_SIZE = Math.max(2, Math.min(CPU_COUNT - 1, 4));
private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;
private static final int KEEP_ALIVE_SECONDS = 30;
private static final BlockingQueue<Runnable> sPoolWorkQueue =
new LinkedBlockingQueue<Runnable>(128);
public static final Executor THREAD_POOL_EXECUTOR;
static {
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
CORE_POOL_SIZE, //核心線程2-4
MAXIMUM_POOL_SIZE, //最大線程CPU_COUNT * 2 + 1,8核CPU就是17,非核心線程就是13-15
KEEP_ALIVE_SECONDS, TimeUnit.SECONDS,//非核心線程超時(shí)30S
sPoolWorkQueue, //最大為128容量的LinkedBlockingQueue
sThreadFactory);
threadPoolExecutor.allowCoreThreadTimeOut(true);
THREAD_POOL_EXECUTOR = threadPoolExecutor;
}
這就是通過AsyncTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR,”")聲明使用并行處理所使用的線程池。
工作線程實(shí)現(xiàn)是:FutureTask+Callable的方式,這種方式的執(zhí)行有返回值。
使用handler來切換UI線程。
優(yōu)點(diǎn):提供一種非常簡單的異步處理機(jī)制。
缺點(diǎn):
- Android 3.0之后默認(rèn)情況下,任務(wù)是串行處理,一旦其中的某個(gè)AsyncTask執(zhí)行時(shí)間過長,隊(duì)列中的其他剩余AsyncTask都處于阻塞狀態(tài),必須等到該任務(wù)執(zhí)行完畢之后才能夠有機(jī)會執(zhí)行下一個(gè)任務(wù)。不過可以使用AsyncTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR,”")強(qiáng)制指定AsyncTask使用線程池并發(fā)調(diào)度任務(wù)。
- 終止工作線程任務(wù)比較麻煩,雖然AsyncTaks有提供cancel()的方法,但是它并不是直接終止工作線程的執(zhí)行,而是作為一個(gè)flag配合在doInBackground()的代碼中不斷的添加程序是否被中止的判斷邏輯。一旦任務(wù)被成功中止,AsyncTask就不會繼續(xù)調(diào)用onPostExecute(),而是回調(diào)onCancelled()。
- AsyncTask經(jīng)常被作為內(nèi)部類使用,很容易造成內(nèi)部類持有外部類引用造成內(nèi)存短暫泄漏的情況。

綜上分析:單一小任務(wù)就用默認(rèn)串行處理就行,稍微多一點(diǎn)的并發(fā)任務(wù)可以用打開并行線程池來處理,但是大量并發(fā)就不行了,很明顯它對應(yīng)的線程池的線程是有限的,并不像CacheThreadPool那樣。
2.3 HandlerThread
這個(gè)比較簡單,HandlerThread 就是擁有l(wèi)ooper的子線程 ,配合Handler掛上HandlerThread對應(yīng)的loop來使用。讓大量任務(wù)工作在子線程且排隊(duì)執(zhí)行。
使用場景:
HandlerThread比較合適處理那些在工作線程執(zhí)行,但是花費(fèi)時(shí)間不長的任務(wù)(這里Android性能優(yōu)化典范(五)中寫的是花費(fèi)時(shí)間長的任務(wù),我有不同意見,見缺點(diǎn)分析)。我們只需要把任務(wù)發(fā)送給HandlerThread,然后就只需要等待任務(wù)執(zhí)行結(jié)束的時(shí)候通知返回到主線程就好了。
優(yōu)點(diǎn):
- 單一工作線程對多任務(wù)進(jìn)行串行管理最簡單高效的實(shí)現(xiàn),當(dāng)然你也可以使用SingleThreadPool,但是handler發(fā)送消息的串行可以按時(shí)間來排序,順序可以調(diào)整,而SingleThreadPool應(yīng)該就不能排序了,而AsyncTask的串行方式同樣面臨這個(gè)問題,并且AsyncTask如果是串行,那么后面被阻塞的任務(wù)是被阻塞在主線程。
- 可以給HandlerThread設(shè)置不同的線程優(yōu)先級,CPU會根據(jù)設(shè)置的不同線程優(yōu)先級對所有的線程進(jìn)行調(diào)度優(yōu)化。
缺點(diǎn):任務(wù)在工作線程串行執(zhí)行,那么前面任務(wù)耗時(shí)就會delay到后面的消息處理,雖然是在子線程,但是有些需要同步到主線程的操作就會受到影響。

2.4 IntentService:
默認(rèn)的Service是執(zhí)行在主線程的,可是通常情況下,這很容易影響到程序的繪制性能(搶占了主線程的資源)。除了前面介紹過的AsyncTask與HandlerThread,我們還可以選擇使用IntentService來實(shí)現(xiàn)異步操作。IntentService繼承自普通Service同時(shí)又在內(nèi)部創(chuàng)建了一個(gè)HandlerThread,在onHandlerIntent()的回調(diào)里面處理扔到IntentService的任務(wù)。所以IntentService就不僅僅具備了異步線程的特性,還同時(shí)保留了Service不受主頁面生命周期影響的特點(diǎn)。
特點(diǎn):
- 因?yàn)镮ntentService內(nèi)置的是HandlerThread作為異步線程,所以每一個(gè)交給IntentService的任務(wù)都將以隊(duì)列的方式逐個(gè)被執(zhí)行到,一旦隊(duì)列中有某個(gè)任務(wù)執(zhí)行時(shí)間過長,那么就會導(dǎo)致后續(xù)的任務(wù)都會被延遲處理。
- 通常使用到IntentService的時(shí)候,我們會結(jié)合使用BroadcastReceiver把工作線程的任務(wù)執(zhí)行結(jié)果返回給主UI線程。使用廣播容易引起性能問題,我們可以使用LocalBroadcastManager來發(fā)送只在程序內(nèi)部傳遞的廣播,從而提升廣播的性能。我們也可以使用runOnUiThread()快速回調(diào)到主UI線程。
- 包含正在運(yùn)行的IntentService的程序相比起純粹的后臺程序更不容易被系統(tǒng)殺死,該程序的優(yōu)先級是介于前臺程序與純后臺程序之間的。
適用場景:它的本質(zhì)是在子線程執(zhí)行任務(wù)的服務(wù),因此適合做耗時(shí)操作。

最后結(jié)合自己這一波的學(xué)習(xí)做個(gè)總結(jié):
AsyncTask:單一、耗時(shí)少的任務(wù)且需要返回UI線程做操作的小活可以用,啥都給你封裝好了,照著對應(yīng)方法填就行,另外做進(jìn)度條也很方便。
HandlerThread:執(zhí)行在工作線程,同時(shí)又能夠處理隊(duì)列中的復(fù)雜任務(wù),可以簡單決定執(zhí)行順序,且多組子線程任務(wù)直接還可以設(shè)置線程優(yōu)先級的。它在Android系統(tǒng)中大量使用,什么UIThread、IOThread統(tǒng)統(tǒng)都是HandlerThread。為各種系統(tǒng)服務(wù)提供對應(yīng)的子線程服務(wù)。
IntentService:它就是封裝好的子線程工作的服務(wù),使用服務(wù)的地方可以考慮,一般簡單的異步任務(wù)就別殺雞用牛刀起服務(wù)去干了。
線程池:
FixedThreadPool:并發(fā)不高但每個(gè)任務(wù)耗時(shí)較長的場景用。
CacheThreadPool:高并發(fā)、單個(gè)任務(wù)耗時(shí)短的場景。
ScheduledThreadPool:周期性執(zhí)行和定時(shí)需求的場景。
SingleThreadExecutor:這個(gè)感覺Android中用得應(yīng)該不多,它能實(shí)現(xiàn)的HandlerThread都能實(shí)現(xiàn)。可能純java會比較有使用場景吧。
好了,就寫這么多,歡迎針對我的個(gè)人觀點(diǎn)進(jìn)行討論與批評指正。我靠,又凌晨了,狗命要緊,睡覺!
參考:
線程池之ScheduledThreadPool學(xué)習(xí)
線程池之FixedThreadPool學(xué)習(xí)
線程池之CachedThreadPool學(xué)習(xí)
線程池之SingleThreadPool學(xué)習(xí)
Android性能優(yōu)化典范(五)