一则拷、線程池簡介
Java并發(fā)編程中夹界,我們常常使用以下兩種方法來開啟一個新的線程來并發(fā)完成某些任務(wù):
- 寫一個Thread類的子類冀惭,并重寫run方法染坯,再在主線程中調(diào)用子類的start方法碌上,開啟線程:
public class MyThread extends Thread {
...
@Override
public void run() {
doMyTask();
}
...
public static void main(String[] args) {
Thread t1 = new MyTread("ThreadName");
t1.start(); //啟動線程
}
}
- 寫一個類實現(xiàn)Runnable接口倚评,并實現(xiàn)run方法浦徊,主線程中創(chuàng)建一個Thread實例,在構(gòu)造方法中傳入Runnable實現(xiàn)類的實例天梧,最后調(diào)用start方法盔性,啟動線程:
public class MyRunnable implements Runnable {
...
public void run() {
doMyTask();
}
...
public static void main(String[] args) {
MyRunnable run1 = new MyRunable();
Thread t2 = new Thread(run1); // 構(gòu)造方法中傳入自定義的Runnable
t2.start();
}
}
由于Java的單繼承機(jī)制,方法1導(dǎo)致MyThread類的父類只能是Thread呢岗,不方便擴(kuò)展冕香,同時任務(wù)只能執(zhí)行一次。方法2通過實現(xiàn)接口來構(gòu)造一個可重復(fù)被線程執(zhí)行的Runnable實現(xiàn)類,實現(xiàn)方法更加靈活后豫。
但這兩種方法都不可避免地在主線程中顯式地創(chuàng)建了線程對象暂筝,并在run方法執(zhí)行完成后,會自動銷毀線程(注意:是線程硬贯,不是線程對象)焕襟。然而線程的創(chuàng)建和銷毀是非常消耗資源的,在實際的開發(fā)中饭豹,如果僅僅為了執(zhí)行一次任務(wù)就創(chuàng)建一個線程鸵赖,用完即銷毀,無疑是一種浪費拄衰。為此它褪,線程池的技術(shù)應(yīng)運而生。
類似數(shù)據(jù)庫的連接池翘悉,程序每次需要接入數(shù)據(jù)庫時茫打,就從連接池中獲取一個空閑連接,使用完成后再歸還給連接池妖混。線程池管理并提供程序并發(fā)執(zhí)行任務(wù)所需的線程資源老赤。本文主要介紹有關(guān)Java線程池的基本原理以及常用的線程池對象實例。
二制市、線程池的核心類——ThreadPoolExecutor類
通過如下方法抬旺,可以方便地構(gòu)建一個固定大小的線程池實例。調(diào)用execute方法祥楣,傳入(提交)Runnable對象執(zhí)行任務(wù)开财。
public class MyRunnable implements Runnable {
...
public void run() {
doMyTask();
}
...
public static void main(String[] args) {
MyRunnable run1 = new MyRunable();
int threadPoolSize = 5; // 固定大小線程池的容量
ExecutorService exec = Executors.newFixedThreadPool(threadPoolSize);
exec.execute(run1); // 執(zhí)行任務(wù)
}
}
進(jìn)入Executors類中,可以看到返回線程池實例的靜態(tài)方法newFixedThreadPool調(diào)用的是java.util.concurrent.ThreadPoolExecutor類的構(gòu)造方法误褪,并返回一個ExecutorService實例责鳍。ThreadPoolExecutor類是java線程池最核心的類,通過傳入其構(gòu)造方法的參數(shù)不同兽间,JDK實現(xiàn)了多種類型線程池历葛。雖然ThreadPoolExecutor提供了四個構(gòu)造方法,但調(diào)用的都是參數(shù)最多的那一個:
public ThreadPoolExecutor(int corePoolSize, // 核心線程數(shù)量
int maximumPoolSize, // 最大線程數(shù)量(核心+工作)
long keepAliveTime, // 空閑工作線程的存活時間
TimeUnit unit, // 時間單位
BlockingQueue<Runnable> workQueue, // 工作隊列
ThreadFactory threadFactory, // 線程的創(chuàng)建工廠
RejectedExecutionHandler handler // 拒絕策略)
2.1 核心線程與工作線程
線程池中的線程分為核心線程和工作線程渡八。corePoolSize為允許的最大核心線程數(shù)量啃洋,maximumPoolSize-corePoolSize為工作線程最大數(shù)量传货。核心線程與工作線程的區(qū)別就像一個公司的正式員工和臨時工,公司會一直“養(yǎng)”著正式員工工作宏娄,當(dāng)工作太多正式員工不夠時问裕,才會聘請臨時工。
大部分線程池都會維護(hù)一定數(shù)量的核心線程(Cached線程池里都是工作線程孵坚,后面會介紹)粮宛,當(dāng)所有核心線程都忙碌,且工作隊列已滿卖宠,若再有任務(wù)提交的話巍杈,就會在maximumPoolSize允許的范圍內(nèi),創(chuàng)建工作線程來執(zhí)行任務(wù)(任務(wù)提交的具體執(zhí)行步驟后面會提)扛伍。若有工作線程空閑了筷畦,沒事做,并超過了keepAliveTime(單位TimeUnit)時間刺洒,則會自動銷毀鳖宾,就像公司會把多余的臨時工開除一樣。
通過設(shè)置類中的allowCoreThreadTimeOut變量為true逆航,也可以令核心線程空閑超過keepAliveTime的時間后也自動銷毀鼎文。
2.2工作隊列workQueue
當(dāng)調(diào)用線程池的execute方法傳入一個Runnable對象時(本文中稱“提交一個任務(wù)”),并不會立即執(zhí)行該任務(wù)因俐,而是將任務(wù)放置進(jìn)一個阻塞隊列(BlockingQueue)中,線程池中的線程會從隊列中取出任務(wù)拇惋,執(zhí)行,再取新任務(wù)抹剩。這是一種生產(chǎn)者-消費者模式撑帖,目的是將任務(wù)的提交和執(zhí)行解耦開來。基本的工作隊列有三種:無界隊列吧兔,有界隊列和同步移交(Synchronous Handoff)磷仰。工作隊列一般要與線程池的大小搭配選擇。
LinkedBlockingQueue類在Executors中用作newFixedThreadPool和newSingleThreadPool的工作隊列境蔼。這兩個工廠方法調(diào)用LinkedBlockingQueue()來創(chuàng)建一個阻塞隊列實例。從LinkedBlockingQueue類的構(gòu)造方法中可以看出伺通,隊列容量為Integer.MAX_VALUE
,近乎“無窮大”箍土,此時LinkedBlockingQueue類是用作無界隊列。
public LinkedBlockingQueue() {
this(Integer.MAX_VALUE);
}
public LinkedBlockingQueue(int capacity) {
if (capacity <= 0) throw new IllegalArgumentException();
this.capacity = capacity; // 設(shè)置隊列容量
last = head = new Node<E>(null);
}
ArrayBlockingQueue罐监、有界LinkedBlockingQueue以及PriorityBlockingQueue是常用的有界隊列吴藻,能夠避免任務(wù)的無限制提交導(dǎo)致的OOM(OutOfMemory異常)。ArrayBlockingQueue和LinkedBlockingQueue是FIFO隊列(公平)弓柱,消費者線程按照任務(wù)的阻塞順序來從隊列中取出任務(wù)執(zhí)行沟堡。PriorityBlockingQueue中的元素具備優(yōu)先級順序侧但,默認(rèn)根據(jù)自然順序排列,或者在構(gòu)造方法中傳入的Comparator來定義航罗。
SynchronousQueue是一種同步移交隊列禀横。這種隊列非常特殊,內(nèi)部并不維護(hù)一個實際的隊列粥血,而是將生產(chǎn)者線程提供的任務(wù)直接轉(zhuǎn)交給消費者線程進(jìn)行執(zhí)行柏锄。這個隊列用于構(gòu)造newCachedThreadPool,這是一個“無限大”的線程池复亏,提交的任務(wù)會“放”進(jìn)SychronousQueue隊列中趾娃,如果有空閑線程,則直接轉(zhuǎn)交并執(zhí)行缔御,否則新建一個線程執(zhí)行剛提交的任務(wù)抬闷。當(dāng)然也可以在定制的ThreadPoolExecutor中使用同步移交,但是若線程池已滿耕突,則任務(wù)會被拒絕策略處理笤成。
舉個例子描述下三種阻塞隊列的不同。有AB兩組服務(wù)生負(fù)責(zé)洗盤子有勾,A組負(fù)責(zé)把盤子沖水洗干凈疹启,B組負(fù)責(zé)把盤子擦干水。A組把盤子洗干凈后一個個放在盤子架上蔼卡,B組從盤子架上取出盤子擦干凈喊崖。這個盤子架的容量是否有限,就說明是無界隊列或者有界隊列雇逞。同步移交不同之處在于荤懂,A組洗完盤子后,就看看有沒有B組空閑的服務(wù)員塘砸,有的話就把盤子直接交給他讓他擦干凈节仿,沒有的話就拿在手上等待是否有空閑的B組服務(wù)員,一段時間后還是沒有空閑服務(wù)員掉蔬,于是就把盤子丟進(jìn)垃圾桶里廊宪。整個過程沒有將盤子擺放在盤子架上或者取出的操作。
2.3 拒絕策略
拒絕策略女轿,又稱飽和策略箭启。故名思意,是指線程池達(dá)到飽和狀態(tài)后蛉迹,如果再有新任務(wù)提交傅寡,此時的處理策略。線程池達(dá)到飽和狀態(tài),是指線程數(shù)量最大荐操,并全部處于忙碌狀態(tài)芜抒,且工作隊列被添滿的狀態(tài)。所有的拒絕策略類都實現(xiàn)了RejectedExecutionHandler接口托启,這個接口只有一個方法rejectedExecution
宅倒,用來執(zhí)行策略。常用的拒絕策略有如下幾種:
AbortPolicy是默認(rèn)的拒絕策略驾中。該策略會拋出一個非檢查異常唉堪,以供調(diào)用者捕獲,并進(jìn)一步處理肩民。
// ThreadPoolExecutor的源碼
/**
* The default rejected execution handler
*/
private static final RejectedExecutionHandler defaultHandler = new AbortPolicy();
...
// AbortPolicy類的定義
public static class AbortPolicy implements RejectedExecutionHandler {
public AbortPolicy() { }
// 直接拋出一個unchecked異常
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
throw new RejectedExecutionException("Task " + r.toString() +
" rejected from " +
e.toString());
}
}
DiscardPolicy策略會“一聲不吭”地拋棄提交的新任務(wù)唠亚,從源碼中可以看出,實現(xiàn)的rejectedExecution方法為空持痰,不做任何處理灶搜。
/**
* A handler for rejected tasks that silently discards the
* rejected task.
*/
public static class DiscardPolicy implements RejectedExecutionHandler {
/**
* Creates a {@code DiscardPolicy}.
*/
public DiscardPolicy() { }
/**
* Does nothing, which has the effect of discarding task r.
*
*/
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
}
}
DiscardOldestPolicy策略將隊列中“最老”的任務(wù)拋棄,并重新提交新的任務(wù)工窍。從源碼中可以看出割卖,即poll工作隊列隊列頭元素,然后再次提交新任務(wù)患雏。如果在工作隊列具備優(yōu)先級鹏溯,則拋棄優(yōu)先級最高的任務(wù)。
/**
* A handler for rejected tasks that discards the oldest unhandled
* request and then retries {@code execute}, unless the executor
* is shut down, in which case the task is discarded.
*/
public static class DiscardOldestPolicy implements RejectedExecutionHandler {
/**
* Creates a {@code DiscardOldestPolicy} for the given executor.
*/
public DiscardOldestPolicy() { }
/**
* Obtains and ignores the next task that the executor
* would otherwise execute, if one is immediately available,
* and then retries execution of task r, unless the executor
* is shut down, in which case task r is instead discarded.
*
* @param r the runnable task requested to be executed
* @param e the executor attempting to execute this task
*/
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
if (!e.isShutdown()) {
e.getQueue().poll(); // 拋棄隊列頭元素
e.execute(r); // 重新提交新的任務(wù)
}
}
}
CallerRunsPolicy策略的思想是:誰提交的誰運行淹仑。當(dāng)A線程提交一個新任務(wù)時丙挽,發(fā)現(xiàn)線程池已經(jīng)飽和,則轉(zhuǎn)向執(zhí)行拒絕策略的rejectedExecution方法匀借。CallerRunsPolicy的rejectedExecution方法首先判斷了線程池是否關(guān)閉颜阐,然后直接調(diào)用run方法執(zhí)行任務(wù)。此時執(zhí)行任務(wù)的是A線程吓肋,在處理完成之前凳怨,A線程不能再提交新的任務(wù)。這樣的設(shè)計能夠降低生產(chǎn)者線程任務(wù)提交的速度是鬼,給消費者線程更多的時間處理任務(wù)肤舞。
public static class CallerRunsPolicy implements RejectedExecutionHandler {
public CallerRunsPolicy() { }
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
if (!e.isShutdown()) { // 判斷線程池是否關(guān)閉
r.run(); // 直接執(zhí)行任務(wù)
}
}
}
三、執(zhí)行任務(wù)的核心——execute方法
3.1 ThreadPoolExecutor的超類結(jié)構(gòu)
ThreadPoolExecutor類使用生產(chǎn)者-消費者的設(shè)計模式均蜜,將任務(wù)的提交和執(zhí)行解耦開萨赁。ThreadPoolExecutor類的超類結(jié)構(gòu)如下所示。
該類繼承自AbstractExecutorService抽象類(以下簡稱AES)兆龙,AES類由實現(xiàn)了ExecutorService接口,該接口又繼承自Executor。其中Executor接口中只有一個execute(Runnable command)
方法紫皇。
ExecutorService接口繼承自Executor接口慰安,擴(kuò)展了提交Callable任務(wù)的submit方法、批量提交任務(wù)的invoke*方法以及5個管理線程池生命周期的方法聪铺。
submit方法與execute方法是java線程池中兩個常用的單任務(wù)提交方法化焕,其中submit方法在AbstractExecutorService類中有相關(guān)的實現(xiàn),從源碼中可以看出铃剔,submit方法對提交的對象進(jìn)行了一定的封裝之后撒桨,最終還是調(diào)用execute方法執(zhí)行任務(wù)。
而有關(guān)execute方法的實現(xiàn)键兜,都是在AbstractExecutorService類的子類或者ThreadPoolExecutor類的子類中進(jìn)行實現(xiàn)的凤类。由此可見,這個方法是整個線程池執(zhí)行任務(wù)的核心方法普气。
3.2 execute方法原理
先大致描述下Java線程池的一個任務(wù)的提交流程
本文在拒絕策略那一節(jié)描述了一個任務(wù)會在什么情況下走到被拒絕策略執(zhí)行的這一步谜疤。由上圖可知,主要要滿足3個條件:1现诀、核心線程池已滿夷磕。2、工作隊列已滿仔沿。3坐桩、線程數(shù)量達(dá)到最大(不能再添加線程了)。提交任務(wù)后封锉,程序按照1绵跷、2、3的順序來判斷條件烘浦,只要其中一個條件不滿足抖坪,則該任務(wù)則有望被成功執(zhí)行。之前學(xué)習(xí)時對為什么先判斷工作隊列是否已滿闷叉,再判斷線程數(shù)量是否最大這樣的順序有過困惑擦俐,后來想想應(yīng)該是為了優(yōu)先讓核心線程完成工作,減少創(chuàng)建和銷毀工作線程的消耗握侧。
ThreadPoolExecutor類對executor方法的實現(xiàn)如下:
public void execute(Runnable command) {
if (command == null) // 非空判斷
throw new NullPointerException();
int c = ctl.get(); // 線程池狀態(tài)蚯瞧,一個AutomicInteger變量
// 1、判斷線程數(shù)量是否小于核心線程數(shù)
if (workerCountOf(c) < corePoolSize) {
if (addWorker(command, true))
return;
c = ctl.get();
}
// 2品擎、判斷線程池是否是運行狀態(tài)埋合,并嘗試將任務(wù)添加進(jìn)工作隊列
if (isRunning(c) && workQueue.offer(command)) {
int recheck = ctl.get();
// 重新檢查線程池狀態(tài),如果非運行狀態(tài)萄传,則移除之前的任務(wù)
if (! isRunning(recheck) && remove(command))
// 執(zhí)行拒絕策略
reject(command);
else if (workerCountOf(recheck) == 0)
addWorker(null, false);
}
// 3甚颂、判斷是否可以添加工作線程來執(zhí)行任務(wù)
else if (!addWorker(command, false))
reject(command);
}
代碼可以分成三個部分,即對上述三個條件進(jìn)行判斷(從該方法的注釋也可以看出,參考ThreadPoolExecutor類源碼1335~1354行)振诬。
首先獲取線程池狀態(tài)蹭睡,判斷當(dāng)前線程數(shù)量是否小于核心線程數(shù)量,若滿足則使用addWorker方法添加核心線程赶么。addWorker方法的第二個參數(shù)為true表示創(chuàng)建一個核心線程肩豁。若addWorker方法執(zhí)行成功,則退出execute方法辫呻。
若以上核心線程數(shù)量已滿清钥,或者addWorker方法失敗,則再次獲取線程池狀態(tài)放闺,并在第二部分代碼檢查線程池是否為運行狀態(tài)祟昭,并嘗試將任務(wù)添加進(jìn)工作隊列。工作隊列提供offer和add方法來添加任務(wù)雄人。這兩個方法是BlockedQueue接口的方法从橘,區(qū)別在于前者返回true或false來表示執(zhí)行成功與否,后者成功返回true础钠,失敗則拋異常恰力。若成功添加任務(wù)進(jìn)工作隊列,還需根據(jù)二次判斷線程池狀態(tài)(參考1344~1349行注釋可知旗吁,這是為了并發(fā)考慮的)的結(jié)果踩萎,來決定是回滾這個這個任務(wù)添加過程,即移除(remove方法)剛剛添加的任務(wù)很钓,并交給拒絕策略處理(reject方法)香府,還是addWorker(null, false)
(這里不太明白,待以后完善)码倦。
如果工作隊列已滿企孩,則嘗試使用addWorker方法添加工作線程(第二個參數(shù)傳false)。如果失敗袁稽,就只能使用reject方法將該任務(wù)交給拒絕策略處理勿璃。
execute的關(guān)鍵方法是addWorker方法。就像方法名所述推汽,新建的線程被包裝成一個Worker類并添加進(jìn)一個Set集合中來執(zhí)行任務(wù)补疑。Worker是ThreadPoolExecutor類的一個final內(nèi)部類。代碼片段如下:
private final class Worker extends AbstractQueuedSynchronizer implements Runnable
{
final Thread thread;
/** Initial task to run. Possibly null. */
Runnable firstTask;
/** Per-thread task counter */
volatile long completedTasks;
/**
* Creates with given first task and thread from ThreadFactory.
* @param firstTask the first task (null if none)
*/
Worker(Runnable firstTask) {
setState(-1); // inhibit interrupts until runWorker
this.firstTask = firstTask;
this.thread = getThreadFactory().newThread(this);
}
Worker類有點意思歹撒,它繼承自AbstractQueuedSynchronizer類(AQS)(《Java并發(fā)核心類——AbstractQueuedSynchronizer類》)莲组。它的這個“爹”很有背景,是FutureTask類暖夭、ReentrantLock類锹杈、信號量Semaphore類以及閉鎖CountDownLatch類等很多并發(fā)工具類的底層實現(xiàn)撵孤,可以說java.util.concurrent包下絕大多數(shù)類都與AQS有一腿。在此就不繼續(xù)深入了嬉橙。
ThreadPoolExecutor類直接使用Worker對象來執(zhí)行任務(wù)早直。Worker類實現(xiàn)了Runnable方法,并且封裝了一個Thread對象市框,一個Thread對象對應(yīng)一個Worker,如果由于拋出Exception導(dǎo)致線程死亡糕韧,對應(yīng)的Worker對象同時也會從線程池中清除枫振。Worker的run方法調(diào)用runWorker方法,該方法會先將該Woker對象lock萤彩,然后進(jìn)入一個while循環(huán)粪滤,調(diào)用有阻塞功能的getTask方法不斷從阻塞隊列中取任務(wù),并進(jìn)行執(zhí)行雀扶,直到發(fā)生異痴刃。或者getTask返回null。實際上調(diào)用Runnable任務(wù)的run方法前后愚墓,還調(diào)用了beforeExecute和afterExecute方法予权,并進(jìn)行了比較復(fù)雜的異常處理,有興趣可以參考源碼浪册,滿精巧的扫腺。
至此,在回顧下本節(jié)開頭所述的任務(wù)提交流程村象,應(yīng)該會更加清晰笆环、易于理解。
四厚者、常見的幾種線程池
平時開發(fā)中一般通過調(diào)用java.util.concurrent.Executors類的靜態(tài)工廠方法躁劣,根據(jù)需求創(chuàng)建所需的線程池實例。常用的有以下幾種:
4.1 固定容量線程池
通過newFixedThreadPool(int nThreads)
方法或newFixedThreadPool(int nThreads, ThreadFactory threadFactory)
創(chuàng)建固定容量線程池库菲。傳入的int參數(shù)即為核心線程數(shù)量又是最大線程數(shù)账忘。線程工廠可以指定。
public static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory) {
return new ThreadPoolExecutor(nThreads, nThreads, // 核心線程數(shù)量與最大線程數(shù)量一致
0L, TimeUnit.MILLISECONDS, // 線程永不“過期”蝙昙,一致存活
new LinkedBlockingQueue<Runnable>(), // 工作隊列為無界隊列
threadFactory); // 指定線程工廠闪萄,若這個參數(shù)沒有,則使用默認(rèn)線程工廠
}
由于最大線程數(shù)量與核心線程數(shù)一樣奇颠,因此固定線程池中所有的線程都是核心線程败去,且在停止線程池之前一直存活。當(dāng)線程數(shù)量最大烈拒,且全部忙碌時圆裕,任務(wù)被添加進(jìn)工作隊列广鳍。由于是無界隊列,則任務(wù)可以“無限制”添加吓妆。
4.2 Cached線程池
Cached線程池使用CachedThreadPool方法創(chuàng)建赊时。可選是否指定線程工廠
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE, // 核心線程數(shù)0,
60L, TimeUnit.SECONDS, // 空閑線程存活時間60s
new SynchronousQueue<Runnable>()); // 同步提交
}
Cached線程池核心線程數(shù)為0行拢,線程最大數(shù)量“無限”祖秒,因此Cached線程池中都是工作線程,且空閑線程存活時間為60s舟奠。阻塞隊列為同步提交隊列竭缝,即任務(wù)提交必須有一個空閑線程來接才能成功。由于Cached線程池“無限大”沼瘫,因此任務(wù)也可以無限制提交抬纸,并且不需要排隊執(zhí)行。
4.3 單線程線程池
這個線程池通過newSingleThreadExecutor方法創(chuàng)建耿戚,核心線程數(shù)與最大線程數(shù)都指定為1湿故,并搭配無界工作隊列使用。因此提交的任務(wù)一FIFO的順序一個個執(zhí)行膜蛔。
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1, // 核心線程數(shù)與最大線程數(shù)為1
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>())); // 無界的工作隊列
}
4.4 定時線程池
顧名思義坛猪,定時線程池可以規(guī)定任務(wù)何時執(zhí)行。使用newScheduledThreadPool方法創(chuàng)建飞几,該方法返回一個ScheduledThreadPoolExecutor類實例砚哆。這個類是ThreadPoolExecutor類的子類,并實現(xiàn)了ScheduledExecutorService接口屑墨,這個接口呢又是ExecutorService接口躁锁,增加了一系列的schedule**定時調(diào)度方法。
// Executors工廠方法
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
return new ScheduledThreadPoolExecutor(corePoolSize);
}
// ScheduledThreadPoolExecutor類構(gòu)造方法之一
public ScheduledThreadPoolExecutor(int corePoolSize) {
super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
new DelayedWorkQueue());
}
定時線程池的通過傳入?yún)?shù)指定核心線程池數(shù)量卵史,線程池最大數(shù)量“無限”战转,搭配一個DelayedWorkQueue的阻塞隊列使用,這個阻塞隊列類是ScheduledThreadPoolExecutor類的靜態(tài)內(nèi)部類以躯。
以上線程池的拒絕策略均為默認(rèn)的AbortPolicy策略槐秧,但一般情況下都走不到被拒絕策略處理這一步,畢竟他們要不是線程數(shù)量“無限大”忧设,要不就是工作隊列“無限大”提交任務(wù)刁标。
4.5 線程池線程數(shù)量的配置
線程數(shù)量的配置要根據(jù)CPU的核數(shù)以及處理任務(wù)的類型來選擇。若CPU有N個核(Runtime.getRuntime().availableProcessors()
獲得可用的CPU核數(shù))址晕,IO密集型任務(wù)可以配置2*N的線程數(shù)量膀懈,CPU密集型任務(wù)可以配置N+1的線程數(shù)量。若線程池實例是通過Executors工廠方法創(chuàng)建谨垃,則可以強(qiáng)轉(zhuǎn)成ThreadPoolExecutor類型启搂,然后使用下面這些方法重新配置線程池硼控。
五、總結(jié)
使用線程池進(jìn)行并發(fā)程序開發(fā)有諸多好處:
- 降低資源消耗胳赌。通過重復(fù)利用已創(chuàng)建的線程降低線程創(chuàng)建和銷毀造成的消耗牢撼。
- 提高響應(yīng)速度。當(dāng)任務(wù)到達(dá)時疑苫,任務(wù)可以不需要等到線程創(chuàng)建就能立即執(zhí)行熏版。
- 提高線程的可管理性。線程是稀缺資源缀匕,如果無限制的創(chuàng)建纳决,不僅會消耗系統(tǒng)資源,還會降低系統(tǒng)的穩(wěn)定性乡小,使用線程池可以進(jìn)行統(tǒng)一的分配,調(diào)優(yōu)和監(jiān)控饵史。
本文粗略地介紹了有關(guān)Java線程池中的相關(guān)知識满钟。首先介紹線程池核心類ThreadPoolExecutor類具備的功能組件,然后簡要介紹了線程池任務(wù)提交方法execute方法的工作流程胳喷,最后介紹了常用的幾種線程池實例的配置詳細(xì)湃番。這些知識算是對自已學(xué)習(xí)并發(fā)編程的一點小總結(jié)吧。
文中多數(shù)使用“無限”這個詞吭露,并使用雙引號引起吠撮,是因為這個詞表示Integer.MAX_VALUE
,為十進(jìn)制21_4748_3647讲竿。雖然這個數(shù)已經(jīng)非常大了泥兰,接近于無限,但卻并不是無限题禀,故用引號引起鞋诗。
六、參考
- 《Java并發(fā)編程實戰(zhàn)》
- 深入理解Java之線程池
- 聊聊并發(fā)(七)——Java中的阻塞隊列
- Java線程池詳解 我室友ruheng博客迈嘹,強(qiáng)烈推薦削彬!