我們使用線程的時候就去創(chuàng)建一個線程榔至,這樣實現(xiàn)起來非常簡便耗绿,但是就會有一個問題:
如果并發(fā)的線程數(shù)量很多杨箭,并且每個線程都是執(zhí)行一個時間很短的任務(wù)就結(jié)束了膝蜈,這樣頻繁創(chuàng)建線程就會大大降低系統(tǒng)的效率鹅心,因為頻繁創(chuàng)建線程和銷毀線程需要時間吕粗。
那么有沒有一種辦法使得線程可以復(fù)用,就是執(zhí)行完一個任務(wù)旭愧,并不被銷毀颅筋,而是可以繼續(xù)執(zhí)行其他的任務(wù)虐秋?
在Java中可以通過線程池來達到這樣的效果。今天我們就來詳細講解一下Java的線程池垃沦,首先我們從最核心的ThreadPoolExecutor類中的方法講起客给,然后再講述它的實現(xiàn)原理,接著給出了它的使用示例肢簿,最后討論了一下如何合理配置線程池的大小靶剑。
以下是本文的目錄大綱:
一.Java中的ThreadPoolExecutor類
二.深入剖析線程池實現(xiàn)原理
三.使用示例
四.如何合理配置線程池的大小
若有不正之處請多多諒解,并歡迎批評指正池充。
一.Java中的ThreadPoolExecutor類
二.深入剖析線程池實現(xiàn)原理
在上一節(jié)我們從宏觀上介紹了ThreadPoolExecutor桩引,下面我們來深入解析一下線程池的具體實現(xiàn)原理,將從下面幾個方面講解:
1.線程池狀態(tài)
2.任務(wù)的執(zhí)行
3.線程池中的線程初始化
4.任務(wù)緩存隊列及排隊策略
5.任務(wù)拒絕策略
6.線程池的關(guān)閉
7.線程池容量的動態(tài)調(diào)整
1.線程池狀態(tài)
在ThreadPoolExecutor中定義了一個Volatile變量收夸,另外定義了幾個static final變量表示線程池的各個狀態(tài):
volatile int runState;
static final int RUNNING = 0;
static final int SHUTDOWN = 1;
static final int STOP = 2;
static final int TERMINATED = 3;
runState表示當前線程池的狀態(tài)坑匠,它是一個volatile變量用來保證線程之間的可見性;
下面的幾個static final變量表示runState可能的幾個取值卧惜。
當創(chuàng)建線程池后厘灼,初始時,線程池處于RUNNING狀態(tài)咽瓷;
如果調(diào)用了shutdown()方法设凹,則線程池處于SHUTDOWN狀態(tài),此時線程池不能夠接受新的任務(wù)茅姜,它會等待所有任務(wù)執(zhí)行完畢闪朱;
如果調(diào)用了shutdownNow()方法,則線程池處于STOP狀態(tài)钻洒,此時線程池不能接受新的任務(wù)奋姿,并且會去嘗試終止正在執(zhí)行的任務(wù);
當線程池處于SHUTDOWN或STOP狀態(tài)素标,并且所有工作線程已經(jīng)銷毀称诗,任務(wù)緩存隊列已經(jīng)清空或執(zhí)行結(jié)束后,線程池被設(shè)置為TERMINATED狀態(tài)糯钙。
2.任務(wù)的執(zhí)行
在了解將任務(wù)提交給線程池到任務(wù)執(zhí)行完畢整個過程之前粪狼,我們先來看一下ThreadPoolExecutor類中其他的一些比較重要成員變量:
private final BlockingQueue<Runnable> workQueue; //任務(wù)緩存隊列退腥,用來存放等待執(zhí)行的任務(wù)
private final ReentrantLock mainLock = new ReentrantLock(); //線程池的主要狀態(tài)鎖任岸,對線程池狀態(tài)(比如線程池大小
//、runState等)的改變都要使用這個鎖
private final HashSet<Worker> workers = new HashSet<Worker>(); //用來存放工作集
private volatile long keepAliveTime; //線程存活時間
private volatile boolean allowCoreThreadTimeOut; //是否允許為核心線程設(shè)置存活時間
private volatile int corePoolSize; //核心池的大薪屏酢(即線程池中的線程數(shù)目大于這個參數(shù)時享潜,提交的任務(wù)會被放進任務(wù)緩存隊列)
private volatile int maximumPoolSize; //線程池最大能容忍的線程數(shù)
private volatile int poolSize; //線程池中當前的線程數(shù)
private volatile RejectedExecutionHandler handler; //任務(wù)拒絕策略
private volatile ThreadFactory threadFactory; //線程工廠,用來創(chuàng)建線程
private int largestPoolSize; //用來記錄線程池中曾經(jīng)出現(xiàn)過的最大線程數(shù)
private long completedTaskCount; //用來記錄已經(jīng)執(zhí)行完畢的任務(wù)個數(shù)
每個變量的作用都已經(jīng)標明出來了嗅蔬,這里要重點解釋一下corePoolSize剑按、maximumPoolSize疾就、largestPoolSize三個變量。
corePoolSize在很多地方被翻譯成核心池大小艺蝴,其實我的理解這個就是線程池的大小猬腰。
largestPoolSize只是一個用來起記錄作用的變量,用來記錄線程池中曾經(jīng)有過的最大線程數(shù)目猜敢,跟線程池的容量沒有任何關(guān)系姑荷。
下面我們進入正題,看一下任務(wù)從提交到最終執(zhí)行完畢經(jīng)歷了哪些過程缩擂。
在ThreadPoolExecutor類中鼠冕,最核心的任務(wù)提交方法是execute()方法,雖然通過submit也可以提交任務(wù)胯盯,但是實際上submit方法里面最終調(diào)用的還是execute()方法懈费,所以我們只需要研究execute()方法的實現(xiàn)原理即可:
public void execute(Runnable command) {
if (command == null)
throw new NullPointerException();
if (poolSize >= corePoolSize || !addIfUnderCorePoolSize(command)) {
if (runState == RUNNING && workQueue.offer(command)) {
if (runState != RUNNING || poolSize == 0)
ensureQueuedTaskHandled(command);
}
else if (!addIfUnderMaximumPoolSize(command))
reject(command); // is shutdown or saturated
}
}
上面的代碼可能看起來不是那么容易理解,下面我們一句一句解釋:
首先博脑,判斷提交的任務(wù)command是否為null憎乙,若是null,則拋出空指針異常叉趣;
接著是這句寨闹,這句要好好理解一下:
if (poolSize >= corePoolSize || !addIfUnderCorePoolSize(command))
由于是或條件運算符,所以先計算前半部分的值君账,如果線程池中當前線程數(shù)不小于核心池大小,那么就會直接進入下面的if語句塊了乡数。
如果線程池中當前線程數(shù)小于核心池大小,則接著執(zhí)行后半部分净赴,也就是執(zhí)行:
addIfUnderCorePoolSize(command)
如果執(zhí)行完addIfUnderCorePoolSize這個方法返回false,則繼續(xù)執(zhí)行下面的if語句塊玖翅,否則整個方法就直接執(zhí)行完畢了。
如果執(zhí)行完addIfUnderCorePoolSize這個方法返回false金度,然后接著判斷:
if (runState == RUNNING && workQueue.offer(command))
如果當前線程池處于RUNNING狀態(tài),則將任務(wù)放入任務(wù)緩存隊列猜极;如果當前線程池不處于RUNNING狀態(tài)或者任務(wù)放入緩存隊列失敗,則執(zhí)行:
addIfUnderMaximumPoolSize(command)
如果執(zhí)行addIfUnderMaximumPoolSize方法失敗跟伏,則執(zhí)行reject()方法進行任務(wù)拒絕處理翩瓜。
回到前面:
if (runState == RUNNING && workQueue.offer(command))
這句的執(zhí)行携龟,如果說當前線程池處于RUNNING狀態(tài)且將任務(wù)放入任務(wù)緩存隊列成功,則繼續(xù)進行判斷:
if (runState != RUNNING || poolSize == 0)
這句判斷是為了防止在將此任務(wù)添加進任務(wù)緩存隊列的同時其他線程突然調(diào)用shutdown或者shutdownNow方法關(guān)閉了線程池的一種應(yīng)急措施峡蟋。如果是這樣就執(zhí)行:
ensureQueuedTaskHandled(command)
進行應(yīng)急處理浮定,從名字可以看出是保證 添加到任務(wù)緩存隊列中的任務(wù)得到處理。
我們接著看2個關(guān)鍵方法的實現(xiàn):addIfUnderCorePoolSize和addIfUnderMaximumPoolSize:
private boolean addIfUnderCorePoolSize(Runnable firstTask) {
Thread t = null;
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
if (poolSize < corePoolSize && runState == RUNNING)
t = addThread(firstTask); //創(chuàng)建線程去執(zhí)行firstTask任務(wù)
} finally {
mainLock.unlock();
}
if (t == null)
return false;
t.start();
return true;
}
這個是addIfUnderCorePoolSize方法的具體實現(xiàn)层亿,從名字可以看出它的意圖就是當?shù)陀诤诵某源笮r執(zhí)行的方法桦卒。下面看其具體實現(xiàn),首先獲取到鎖匿又,因為這地方涉及到線程池狀態(tài)的變化方灾,先通過if語句判斷當前線程池中的線程數(shù)目是否小于核心池大小,有朋友也許會有疑問:前面在execute()方法中不是已經(jīng)判斷過了嗎碌更,只有線程池當前線程數(shù)目小于核心池大小才會執(zhí)行addIfUnderCorePoolSize方法的裕偿,為何這地方還要繼續(xù)判斷?原因很簡單痛单,前面的判斷過程中并沒有加鎖嘿棘,因此可能在execute方法判斷的時候poolSize小于corePoolSize,而判斷完之后旭绒,在其他線程中又向線程池提交了任務(wù)鸟妙,就可能導(dǎo)致poolSize不小于corePoolSize了,所以需要在這個地方繼續(xù)判斷挥吵。然后接著判斷線程池的狀態(tài)是否為RUNNING重父,原因也很簡單,因為有可能在其他線程中調(diào)用了shutdown或者shutdownNow方法忽匈。然后就是執(zhí)行
t = addThread(firstTask);
這個方法也非常關(guān)鍵房午,傳進去的參數(shù)為提交的任務(wù),返回值為Thread類型丹允。然后接著在下面判斷t是否為空郭厌,為空則表明創(chuàng)建線程失敗(即poolSize>=corePoolSize或者runState不等于RUNNING)雕蔽,否則調(diào)用t.start()方法啟動線程折柠。
我們來看一下addThread方法的實現(xiàn):
private Thread addThread(Runnable firstTask) {
Worker w = new Worker(firstTask);
Thread t = threadFactory.newThread(w); //創(chuàng)建一個線程,執(zhí)行任務(wù)
if (t != null) {
w.thread = t; //將創(chuàng)建的線程的引用賦值為w的成員變量
workers.add(w);
int nt = ++poolSize; //當前線程數(shù)加1
if (nt > largestPoolSize)
largestPoolSize = nt;
}
return t;
}
在addThread方法中萎羔,首先用提交的任務(wù)創(chuàng)建了一個Worker對象液走,然后調(diào)用線程工廠threadFactory創(chuàng)建了一個新的線程t,然后將線程t的引用賦值給了Worker對象的成員變量thread缘眶,接著通過workers.add(w)將Worker對象添加到工作集當中巷懈。
下面我們看一下Worker類的實現(xiàn):
private final class Worker implements Runnable {
private final ReentrantLock runLock = new ReentrantLock();
private Runnable firstTask;
volatile long completedTasks;
Thread thread;
Worker(Runnable firstTask) {
this.firstTask = firstTask;
}
boolean isActive() {
return runLock.isLocked();
}
void interruptIfIdle() {
final ReentrantLock runLock = this.runLock;
if (runLock.tryLock()) {
try {
if (thread != Thread.currentThread())
thread.interrupt();
} finally {
runLock.unlock();
}
}
}
void interruptNow() {
thread.interrupt();
}
private void runTask(Runnable task) {
final ReentrantLock runLock = this.runLock;
runLock.lock();
try {
if (runState < STOP &&
Thread.interrupted() &&
runState >= STOP)
boolean ran = false;
beforeExecute(thread, task); //beforeExecute方法是ThreadPoolExecutor類的一個方法顶燕,沒有具體實現(xiàn)涌攻,用戶可以根據(jù)
//自己需要重載這個方法和后面的afterExecute方法來進行一些統(tǒng)計信息恳谎,比如某個任務(wù)的執(zhí)行時間等
try {
task.run();
ran = true;
afterExecute(task, null);
++completedTasks;
} catch (RuntimeException ex) {
if (!ran)
afterExecute(task, ex);
throw ex;
}
} finally {
runLock.unlock();
}
}
public void run() {
try {
Runnable task = firstTask;
firstTask = null;
while (task != null || (task = getTask()) != null) {
runTask(task);
task = null;
}
} finally {
workerDone(this); //當任務(wù)隊列中沒有任務(wù)時因痛,進行清理工作
}
}
}
它實際上實現(xiàn)了Runnable接口鸵膏,因此上面的Thread t = threadFactory.newThread(w);效果跟下面這句的效果基本一樣:
Thread t = new Thread(w);
相當于傳進去了一個Runnable任務(wù)谭企,在線程t中執(zhí)行這個Runnable赞咙。
既然Worker實現(xiàn)了Runnable接口攀操,那么自然最核心的方法便是run()方法了:
public void run() {
try {
Runnable task = firstTask;
firstTask = null;
while (task != null || (task = getTask()) != null) {
runTask(task);
task = null;
}
} finally {
workerDone(this);
}
}
從run方法的實現(xiàn)可以看出秸抚,它首先執(zhí)行的是通過構(gòu)造器傳進來的任務(wù)firstTask剥汤,在調(diào)用runTask()執(zhí)行完firstTask之后吭敢,在while循環(huán)里面不斷通過getTask()去取新的任務(wù)來執(zhí)行,那么去哪里取呢欲低?自然是從任務(wù)緩存隊列里面去取砾莱,getTask是ThreadPoolExecutor類中的方法,并不是Worker類中的方法聚假,下面是getTask方法的實現(xiàn):
Runnable getTask() {
for (;;) {
try {
int state = runState;
if (state > SHUTDOWN)
return null;
Runnable r;
if (state == SHUTDOWN) // Help drain queue
r = workQueue.poll();
else if (poolSize > corePoolSize || allowCoreThreadTimeOut) //如果線程數(shù)大于核心池大小或者允許為核心池線程設(shè)置空閑時間膘格,
//則通過poll取任務(wù)瘪贱,若等待一定的時間取不到任務(wù)政敢,則返回null
r = workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS);
else
r = workQueue.take();
if (r != null)
return r;
if (workerCanExit()) { //如果沒取到任務(wù)喷户,即r為null褪尝,則判斷當前的worker是否可以退出
if (runState >= SHUTDOWN) // Wake up others
interruptIdleWorkers(); //中斷處于空閑狀態(tài)的worker
return null;
}
// Else retry
} catch (InterruptedException ie) {
// On interruption, re-check runState
}
}
}
在getTask中河哑,先判斷當前線程池狀態(tài)璃谨,如果runState大于SHUTDOWN(即為STOP或者TERMINATED)鲤妥,則直接返回null棉安。
如果runState為SHUTDOWN或者RUNNING贡耽,則從任務(wù)緩存隊列取任務(wù)鹊汛。
如果當前線程池的線程數(shù)大于核心池大小corePoolSize或者允許為核心池中的線程設(shè)置空閑存活時間刁憋,則調(diào)用poll(time,timeUnit)來取任務(wù)职祷,這個方法會等待一定的時間届囚,如果取不到任務(wù)就返回null意系。
然后判斷取到的任務(wù)r是否為null蛔添,為null則通過調(diào)用workerCanExit()方法來判斷當前worker是否可以退出迎瞧,我們看一下workerCanExit()的實現(xiàn):
private boolean workerCanExit() {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
boolean canExit;
//如果runState大于等于STOP凶硅,或者任務(wù)緩存隊列為空了
//或者 允許為核心池線程設(shè)置空閑存活時間并且線程池中的線程數(shù)目大于1
try {
canExit = runState >= STOP ||
workQueue.isEmpty() ||
(allowCoreThreadTimeOut &&
poolSize > Math.max(1, corePoolSize));
} finally {
mainLock.unlock();
}
return canExit;
}
也就是說如果線程池處于STOP狀態(tài)足绅、或者任務(wù)隊列已為空或者允許為核心池線程設(shè)置空閑存活時間并且線程數(shù)大于1時氢妈,允許worker退出首量。如果允許worker退出加缘,則調(diào)用interruptIdleWorkers()中斷處于空閑狀態(tài)的worker琅捏,我們看一下interruptIdleWorkers()的實現(xiàn):
void interruptIdleWorkers() {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
for (Worker w : workers) //實際上調(diào)用的是worker的interruptIfIdle()方法
w.interruptIfIdle();
} finally {
mainLock.unlock();
}
}
從實現(xiàn)可以看出柄延,它實際上調(diào)用的是worker的interruptIfIdle()方法,在worker的interruptIfIdle()方法中:
void interruptIfIdle() {
final ReentrantLock runLock = this.runLock;
if (runLock.tryLock()) { //注意這里市俊,是調(diào)用tryLock()來獲取鎖的摆昧,因為如果當前worker正在執(zhí)行任務(wù)绅你,鎖已經(jīng)被獲取了忌锯,是無法獲取到鎖的
//如果成功獲取了鎖偶垮,說明當前worker處于空閑狀態(tài)
try {
if (thread != Thread.currentThread())
thread.interrupt();
} finally {
runLock.unlock();
}
}
}
這里有一個非常巧妙的設(shè)計方式似舵,假如我們來設(shè)計線程池,可能會有一個任務(wù)分派線程龙助,當發(fā)現(xiàn)有線程空閑時泌参,就從任務(wù)緩存隊列中取一個任務(wù)交給空閑線程執(zhí)行沽一。但是在這里漓糙,并沒有采用這樣的方式昆禽,因為這樣會要額外地對任務(wù)分派線程進行管理,無形地會增加難度和復(fù)雜度捡硅,這里直接讓執(zhí)行完任務(wù)的線程去任務(wù)緩存隊列里面取任務(wù)來執(zhí)行壮韭。
我們再看addIfUnderMaximumPoolSize方法的實現(xiàn),這個方法的實現(xiàn)思想和addIfUnderCorePoolSize方法的實現(xiàn)思想非常相似琳拨,唯一的區(qū)別在于addIfUnderMaximumPoolSize方法是在線程池中的線程數(shù)達到了核心池大小并且往任務(wù)隊列中添加任務(wù)失敗的情況下執(zhí)行的:
private boolean addIfUnderMaximumPoolSize(Runnable firstTask) {
Thread t = null;
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
if (poolSize < maximumPoolSize && runState == RUNNING)
t = addThread(firstTask);
} finally {
mainLock.unlock();
}
if (t == null)
return false;
t.start();
return true;
}
看到?jīng)]有狱庇,其實它和addIfUnderCorePoolSize方法的實現(xiàn)基本一模一樣恶耽,只是if語句判斷條件中的poolSize < maximumPoolSize不同而已驳棱。
到這里社搅,大部分朋友應(yīng)該對任務(wù)提交給線程池之后到被執(zhí)行的整個過程有了一個基本的了解形葬,下面總結(jié)一下:
1)首先暮的,要清楚corePoolSize和maximumPoolSize的含義冻辩;
2)其次恨闪,要知道Worker是用來起到什么作用的;
3)要知道任務(wù)提交給線程池之后的處理策略老玛,這里總結(jié)一下主要有4點:
如果當前線程池中的線程數(shù)目小于corePoolSize蜡豹,則每來一個任務(wù)镜廉,就會創(chuàng)建一個線程去執(zhí)行這個任務(wù)愚战;
如果當前線程池中的線程數(shù)目>=corePoolSize,則每來一個任務(wù)视乐,會嘗試將其添加到任務(wù)緩存隊列當中佑淀,若添加成功,則該任務(wù)會等待空閑線程將其取出去執(zhí)行谎砾;若添加失斁巴肌(一般來說是任務(wù)緩存隊列已滿)挚币,則會嘗試創(chuàng)建新的線程去執(zhí)行這個任務(wù)妆毕;
如果當前線程池中的線程數(shù)目達到maximumPoolSize笛粘,則會采取任務(wù)拒絕策略進行處理湿硝;
如果線程池中的線程數(shù)量大于 corePoolSize時关斜,如果某線程空閑時間超過keepAliveTime蚤吹,線程將被終止,直至線程池中的線程數(shù)目不大于corePoolSize裁着;如果允許為核心池中的線程設(shè)置存活時間扔罪,那么核心池中的線程空閑時間超過keepAliveTime桶雀,線程也會被終止。
3.線程池中的線程初始化
默認情況下敞咧,創(chuàng)建線程池之后辜腺,線程池中是沒有線程的评疗,需要提交任務(wù)之后才會創(chuàng)建線程百匆。
在實際中如果需要線程池創(chuàng)建之后立即創(chuàng)建線程,可以通過以下兩個方法辦到:
- prestartCoreThread():初始化一個核心線程存璃;
- prestartAllCoreThreads():初始化所有核心線程
下面是這2個方法的實現(xiàn):
public boolean prestartCoreThread() {
return addIfUnderCorePoolSize(null); //注意傳進去的參數(shù)是null
}
public int prestartAllCoreThreads() {
int n = 0;
while (addIfUnderCorePoolSize(null))//注意傳進去的參數(shù)是null
++n;
return n;
}
注意上面?zhèn)鬟M去的參數(shù)是null,根據(jù)第2小節(jié)的分析可知如果傳進去的參數(shù)為null,則最后執(zhí)行線程會阻塞在getTask方法中的
r = workQueue.take();
即等待任務(wù)隊列中有任務(wù)男图。
4.任務(wù)緩存隊列及排隊策略
在前面我們多次提到了任務(wù)緩存隊列逊笆,即workQueue难裆,它用來存放等待執(zhí)行的任務(wù)褂痰。
workQueue的類型為BlockingQueue<Runnable>症虑,通车荆可以取下面三種類型:
1)ArrayBlockingQueue:基于數(shù)組的先進先出隊列,此隊列創(chuàng)建時必須指定大泄淝颉颤绕;
2)LinkedBlockingQueue:基于鏈表的先進先出隊列,如果創(chuàng)建時沒有指定此隊列大小涕烧,則默認為Integer.MAX_VALUE议纯;
3)synchronousQueue:這個隊列比較特殊瞻凤,它不會保存提交的任務(wù)阀参,而是將直接新建一個線程來執(zhí)行新來的任務(wù)蛛壳。
5.任務(wù)拒絕策略###
當線程池的任務(wù)緩存隊列已滿并且線程池中的線程數(shù)目達到maximumPoolSize衙荐,如果還有任務(wù)到來就會采取任務(wù)拒絕策略忧吟,通常有以下四種策略:
ThreadPoolExecutor.AbortPolicy:丟棄任務(wù)并拋出RejectedExecutionException異常。
ThreadPoolExecutor.DiscardPolicy:也是丟棄任務(wù)斩披,但是不拋出異常溜族。
ThreadPoolExecutor.DiscardOldestPolicy:丟棄隊列最前面的任務(wù),然后重新嘗試執(zhí)行任務(wù)(重復(fù)此過程)
ThreadPoolExecutor.CallerRunsPolicy:由調(diào)用線程處理該任務(wù)
6.線程池的關(guān)閉
ThreadPoolExecutor提供了兩個方法垦沉,用于線程池的關(guān)閉煌抒,分別是shutdown()和shutdownNow(),其中:
shutdown():不會立即終止線程池乡话,而是要等所有任務(wù)緩存隊列中的任務(wù)都執(zhí)行完后才終止摧玫,但再也不會接受新的任務(wù)
shutdownNow():立即終止線程池,并嘗試打斷正在執(zhí)行的任務(wù),并且清空任務(wù)緩存隊列诬像,返回尚未執(zhí)行的任務(wù)
7.線程池容量的動態(tài)調(diào)整
ThreadPoolExecutor提供了動態(tài)調(diào)整線程池容量大小的方法:setCorePoolSize()和setMaximumPoolSize(),
setCorePoolSize:設(shè)置核心池大小
setMaximumPoolSize:設(shè)置線程池最大能創(chuàng)建的線程數(shù)目大小
當上述參數(shù)從小變大時,ThreadPoolExecutor進行線程賦值,還可能立即創(chuàng)建新的線程來執(zhí)行任務(wù)否纬。
三.使用示例##
四.如何合理配置線程池的大小##
本節(jié)來討論一個比較重要的話題:如何合理配置線程池大小烙心,僅供參考匙瘪。
一般需要根據(jù)任務(wù)的類型來配置線程池大小:
如果是CPU密集型任務(wù),就需要盡量壓榨CPU,參考值可以設(shè)為 NCPU+1
如果是IO密集型任務(wù),參考值可以設(shè)置為2*NCPU
當然,這只是一個參考值袒哥,具體的設(shè)置還需要根據(jù)實際情況進行調(diào)整,比如可以先將線程池大小設(shè)置為參考值,再觀察任務(wù)運行情況和系統(tǒng)負載、資源利用率來進行適當調(diào)整。