多線程系列歷史文章:
Android/java 多線程(一)-Thread的使用以及源碼分析
Android/java 多線程(二)-Thread的好兄弟Handler
Android/java 多線程(三)-HandlerThread的使用場景及源碼解析
Android/java多線程(四)-IntentService
前面幾篇文章主要都是介紹的單個線程的運用以及原理猜煮,這篇文章開始講解多個線程的運用
簡介
線程池是一個能對多個線程進行統(tǒng)一管理的一套機制育叁,它具有諸多的優(yōu)點:
- 能對線程進行復(fù)用晨横,當(dāng)有空閑的線程莺奸,線程池會復(fù)用這些線程而不會去重新創(chuàng)建格仲,節(jié)省了資源
- 能靈活創(chuàng)建各種線程使用場景,內(nèi)部封裝了常用線程池創(chuàng)建邏輯,并支持自定義線程池邏輯創(chuàng)建
- 能準(zhǔn)確的對每一個線程進行管理胎食,并能讀取每個線程的一寫基礎(chǔ)信息,方便進行一些邏輯處理
缺點:
線程中的數(shù)據(jù)傳遞沒有Handler機制方便
使用場景:
具有諸多耗時任務(wù)的情況允懂,當(dāng)系統(tǒng)為每一個任務(wù)創(chuàng)建一個線程厕怜,會占用系統(tǒng)的大量資源,容易引起界面的卡頓,線程池的復(fù)用機制就很好的解決了這個問題
常用的線程池的使用
自定義線程池
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) {
if (corePoolSize < 0 ||
maximumPoolSize <= 0 ||
maximumPoolSize < corePoolSize ||
keepAliveTime < 0)
throw new IllegalArgumentException();
if (workQueue == null || threadFactory == null || handler == null)
throw new NullPointerException();
this.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.workQueue = workQueue;
this.keepAliveTime = unit.toNanos(keepAliveTime);
this.threadFactory = threadFactory;
this.handler = handler;
}
傳入不同的參數(shù)粥航,實現(xiàn)的效果也不同,下面說一下每個參數(shù)的作用:
- corePoolSize 核心線程數(shù),琅捏,即使空閑時仍保留在池中的線程數(shù),除非設(shè)置了
allowCoreThreadTimeOut
- maximumPoolSize 線程池中允許的最大線程數(shù)
- keepAliveTime 顧名思義,線程終止前的存活時間递雀,即當(dāng)線程數(shù)大于核心線程數(shù)(corePoolSize )時,多余的空閑線程在終止之前等待新任務(wù)的最大時間
- TimeUnit
keepAliveTime
的單位 - workQueue 在執(zhí)行任務(wù)之前用于保存任務(wù)的隊列柄延。 該隊列將僅保存execute方法提交的Runnable任務(wù)。
- ThreadFactory 執(zhí)行程序創(chuàng)建新線程時使用的工廠
- RejectedExecutionHandler 執(zhí)行被阻止時使用的處理程序
這里說一下各個參數(shù)的配置套路,當(dāng)使用execute(Runnable)
方法添加一個任務(wù)到隊列中映之,如果corePoolSize
比maximumPoolSize
小拦焚,隊列滿了后就會去創(chuàng)建新的線程,當(dāng)corePoolSize
與maximumPoolSize
相等時,就會創(chuàng)建一個固定大小的核心線程池杠输。如果將maximumPoolSize
設(shè)置為無限大(例如nteger.MAX_VALUE)赎败,則是創(chuàng)建一個能容納任意數(shù)量任務(wù)的線程池。
ThreadFactory
的作用是用來創(chuàng)建新的線程的蠢甲,它是一個接口,實現(xiàn)它即可創(chuàng)建一個新的線程:
public interface ThreadFactory {
/**
* Constructs a new {@code Thread}. Implementations may also initialize
* priority, name, daemon status, {@code ThreadGroup}, etc.
*
* @param r a runnable to be executed by new thread instance
* @return constructed thread, or {@code null} if the request to
* create a thread is rejected
*/
Thread newThread(Runnable r);
}
通過它僵刮,你可以指定線程的名稱,優(yōu)先級鹦牛,守護進程等搞糕,如果不設(shè)置,則會默認使用Executors.defaultThreadFactory()
曼追,它會創(chuàng)建一個與所有線程與所有相同的ThreadGroup
并且具有相同的優(yōu)先級和非守護進程狀態(tài)NORM_PRIORITY
窍仰。
workQueue
是一個排隊策略,一般有三種:
- 直接切換:對應(yīng)
SynchronousQueue
實現(xiàn)類,將任務(wù)直接交給線程處理礼殊,不需要另外的控制驹吮,通常需要配置一個無限制的maximumPoolSizes
,以避免拒絕掉新提交的任務(wù) - 無界隊列:對應(yīng)
LinkedBlockingDeque
實現(xiàn)類晶伦,它沒有預(yù)定的容量碟狞,當(dāng)有新的任務(wù),會在隊列中等待婚陪,直到加入到corePoolSize的線程中族沃,如果corePoolSize線程一直很忙,也不會去創(chuàng)建新的線程泌参,此時脆淹,最大值最小值對它沒有任何的影響,每個任務(wù)與其他的任務(wù)都是獨立的沽一,不會互相影響 - 有邊界的隊列:例如
ArrayBlockingQueue
實現(xiàn)類,它有助于在使用有限的maxPoolSize時防止資源的耗盡盖溺,但它更難調(diào)整與控制。
一般情況下我們都可以通過java提供的工廠模式來構(gòu)造我們的線程池策略锯玛,主要提供了以下幾種方式:
newCachedThreadPool
特點:
- 線程重用,如果沒有重用的線程,將會創(chuàng)建一個新的線程添加到池中
- 適合執(zhí)行短期異步任務(wù)程序攘残,默認為60秒的線程等待時間拙友,超過就會終止與移除
- 閑置的線程不會消耗資源
源碼配置:
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
使用場景:
它創(chuàng)建了一個無限擴大的線程池,但沒有核心線程池歼郭,因此資源占用少遗契,適合處理一些短期的異步任務(wù)
newFixedThreadPool
特點:
- 創(chuàng)建一個固定數(shù)量的線程池,不能隨時的新建線程病曾,如果隊列已滿牍蜂,提交了新任務(wù),必須等待一個可用的線程
- 線程會一直存在泰涂,直到調(diào)用shutdown方法
源碼配置:
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
使用場景:
它創(chuàng)建了一個核心線程數(shù)與最大線程數(shù)相同的線程池鲫竞,適用于已知任務(wù)數(shù)量,對線程數(shù)量進行限制的場景
newWorkStealingPool
特點:
- 創(chuàng)建使用所有給定并行級別的線程池逼蒙,并且可以使用多個隊列來減少占用
public static ExecutorService newWorkStealingPool() {
return new ForkJoinPool
(Runtime.getRuntime().availableProcessors(),
ForkJoinPool.defaultForkJoinWorkerThreadFactory,
null, true);
}
使用場景:
它能創(chuàng)建一個擁有多個任務(wù)隊列的線程池从绘,可以減少鏈接數(shù)。并且它是默認創(chuàng)建當(dāng)前可用cpu數(shù)量的線程來并行執(zhí)行是牢,因此適用于非常耗時的操作僵井,并且可以并行執(zhí)行
newSingleThreadExecutor
特點:
- 有且只有一個任務(wù)處于活動狀態(tài)
- 先提交的先執(zhí)行,有任務(wù)順序
- 當(dāng)任務(wù)出現(xiàn)異常驳棱,會另創(chuàng)建一個新的線程替換
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
使用場景:
它是一個單例線程批什,并且有執(zhí)行順序,所以適用于有執(zhí)行順序的任務(wù)社搅,并且有且只有一個任務(wù)是執(zhí)行中的
ScheduledThreadPoolExecutor
特點:
- 能設(shè)置延遲時間驻债,能定期執(zhí)行
- 空閑的線程會保留
public ScheduledThreadPoolExecutor(int corePoolSize) {
super(corePoolSize, Integer.MAX_VALUE,
DEFAULT_KEEPALIVE_MILLIS, MILLISECONDS,
new DelayedWorkQueue());
}
使用場景:
它具有定時特性,因此可以用來執(zhí)行周期行的任務(wù)
五大線程池介紹完畢罚渐,此外還有其他的常用方法:
-
execute(Runnable command)
提交任務(wù)却汉,在將來的某個時間點執(zhí)行任務(wù) -
afterExecute(Runnale r,Throwable t)
在指定的Runnale執(zhí)行后調(diào)用此方法 -
beforeExecute(Thread t,Runnable r)
在給定的線程中執(zhí)行給定的Runnable之前調(diào)用方法 -
funalize
當(dāng)執(zhí)行器不再被引用并且沒有線程時,調(diào)用shundown
- shundown 啟動有序關(guān)閉荷并,先前提交的任務(wù)會執(zhí)行合砂,執(zhí)行完畢后關(guān)閉
-
shundownNow
主動停止執(zhí)行中的任務(wù),并返回正在執(zhí)行等待的任務(wù)列表