線程池作用是限制系統(tǒng)中執(zhí)行線程的數(shù)量总滩。根據(jù)系統(tǒng)的環(huán)境情況,可以自動(dòng)或手動(dòng)設(shè)置線程數(shù)量幸逆,達(dá)到運(yùn)行的最佳效果棍辕;少了浪費(fèi)系統(tǒng)資源暮现,多了造成系統(tǒng)擁擠效率不高。用線程池控制線程數(shù)量痢毒,其他線程排隊(duì)等候送矩。一個(gè)任務(wù)執(zhí)行完畢,再?gòu)年?duì)列中取出最前面的任務(wù)開始執(zhí)行哪替。
1. ThreadPoolExecutor
實(shí)際上,一般說的線程池接口菇怀,基本上說的就是ExecutorService凭舶。ExecutorService源碼中有各種API幫助我們使用,ExecutorService接口的默認(rèn)實(shí)現(xiàn)類為ThreadPoolExecutor爱沟。
1.1 線程池的構(gòu)造函數(shù)
構(gòu)造參數(shù)的含義:
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)
1. corePoolSize:核心線程數(shù)量帅霜。
默認(rèn)情況下,在創(chuàng)建了線程池后呼伸,線程池中的線程數(shù)為0身冀,當(dāng)有任務(wù)來之后,就會(huì)創(chuàng)建一個(gè)線程去執(zhí)行任務(wù)括享,當(dāng)線程池中的線程數(shù)目達(dá)到corePoolSize后搂根,就會(huì)把到達(dá)的任務(wù)放到緩存隊(duì)列當(dāng)中;
核心線程默認(rèn)會(huì)一直存活在線程池中铃辖,即使核心線程處于閑置狀態(tài)剩愧。
如果指定ThreadPoolExecutor的allowCoreThreadTimeout=true,那么核心線程若處于閑置狀態(tài)的話娇斩,超過一定的時(shí)間(KeepAliveTime)仁卷,就會(huì)銷毀掉。
2. maximumPoolSize:線程池最大線程數(shù)
線程池中最多能創(chuàng)建多少個(gè)線程犬第。
3. keepAliveTime:非核心線程閑置超時(shí)時(shí)間
默認(rèn)情況下锦积,當(dāng)線程池的線程數(shù)大于corePoolSize時(shí),keepAliveTime才會(huì)起作用歉嗓,直到線程池的線程數(shù)不大于corePoolSize丰介。
4. TimeUnit:keepAliveTime:時(shí)間單位
MILLISECONDS : 1毫秒 、SECONDS : 秒遥椿、MINUTES : 分基矮、HOURS : 小時(shí)、DAYS : 天
5. BlockingQueue<Runnable>:阻塞隊(duì)列
一個(gè)阻塞隊(duì)列冠场,用于存儲(chǔ)等待執(zhí)行的任務(wù)家浇。一般使用LinkedBlockingQueue或者Synchronous(誰坤尼斯
)。維護(hù)著等待執(zhí)行的Runnable對(duì)象碴裙。若corePoolSize滿了之后钢悲,則新添加的任務(wù)會(huì)被添加到這個(gè)隊(duì)列中等待處理点额,如果隊(duì)列滿了,則創(chuàng)建非核心線程執(zhí)行任務(wù)莺琳。
6. ThreadFactory:創(chuàng)建線程的方式
這是一個(gè)接口还棱,Thread newThread(Runnable r),需要實(shí)現(xiàn)該方法惭等。
7. RejectedExecutionHandler:拒絕策略
當(dāng)線程無法執(zhí)行新任務(wù)(一般是由于線程池的線程數(shù)量已經(jīng)達(dá)到最大數(shù)(maximumPoolSize)或者線程池關(guān)閉導(dǎo)致的)默認(rèn)情況下當(dāng)線程池?zé)o法處理新線程珍手,會(huì)拋出RejectedExecutionException。
1.2 線程池的生命周期
- 當(dāng)創(chuàng)建線程池后辞做,初始時(shí)琳要,線程池處于Running狀態(tài)。
- 當(dāng)調(diào)用shutdown()方法秤茅,則線程池處于shutdown狀態(tài)稚补,此時(shí)線程池不能接受新的任務(wù),他會(huì)等待所有任務(wù)執(zhí)行完畢框喳。
- 當(dāng)調(diào)用shutdownNow()方法课幕,則線程處于stop狀態(tài),此時(shí)線程池不能接受新的任務(wù)五垮,并且試圖嘗試終止正在執(zhí)行的任務(wù)乍惊。
- 當(dāng)線程池處于shutdown狀態(tài)或者stop狀態(tài),并且所有工作線程已經(jīng)銷毀拼余,任務(wù)緩存隊(duì)列已經(jīng)清空或者執(zhí)行結(jié)束后污桦,線程池被設(shè)置為terminated(終止
譚母你提的
)。
1.3 線程池任務(wù)執(zhí)行流程
corePoolSize就是核心線程池的大小匙监,maxmumPoolSize就是線程池的任務(wù)量過大時(shí)的一種補(bǔ)救措施凡橱。
- 當(dāng)線程池中線程數(shù)量小于corePoolSize,每來一個(gè)任務(wù)亭姥,就會(huì)創(chuàng)建一個(gè)線程執(zhí)行這個(gè)任務(wù)稼钩。
- 當(dāng)前線程池線程數(shù)量大于等于corePoolSize,則每來一個(gè)任務(wù)达罗。會(huì)嘗試將其添加到任務(wù)緩存隊(duì)列中坝撑,若是添加成功,則該任務(wù)會(huì)等待線程將其取出去執(zhí)行粮揉;若添加失斞怖睢(一般來說任務(wù)緩存隊(duì)列已滿),則會(huì)嘗試創(chuàng)建新的線程執(zhí)行扶认。
- 當(dāng)前線程池線程數(shù)量等于maximumPoolSize侨拦,則會(huì)采取任務(wù)拒絕策略進(jìn)行處理。
- 當(dāng)前線程池線程數(shù)量大于corePoolSize辐宾,如果某線程空閑時(shí)間超過keepAliveTime狱从,線程將會(huì)被終止膨蛮,直到不大于corePoolSize數(shù)量季研。如果允許為核心池(corePoolSize)中的線程設(shè)置存活時(shí)間与涡,那么核心池中的線程空閑時(shí)間超過keepAliveTime豺鼻,線程也會(huì)被終止谬莹。
1.4 線程池中的線程初始化
默認(rèn)情況下附帽,創(chuàng)建線程池之后整胃,線程池是沒有線程的屁使,需要提交任務(wù)才會(huì)創(chuàng)建線程。
在實(shí)際中如果需要線程池創(chuàng)建之后酬蹋,立即創(chuàng)建線程,可以通過:
- java.util.concurrent.ThreadPoolExecutor#prestartCoreThread:初始化一個(gè)核心線程。
- java.util.concurrent.ThreadPoolExecutor#prestartAllCoreThreads:初始化所有核心線程年缎。
1.5 任務(wù)緩存隊(duì)列及排隊(duì)策略
java.util.concurrent.BlockingQueue的特性是:當(dāng)隊(duì)列是空的時(shí)候,從隊(duì)列中獲取或刪除元素將被阻塞堂淡,或者當(dāng)隊(duì)列是滿時(shí),往隊(duì)列中添加元素會(huì)被阻塞。
阻塞隊(duì)列不接受空值,當(dāng)嘗試向隊(duì)列中添加空值時(shí),會(huì)拋出NullPointerException。
阻塞隊(duì)列都是線程安全的,所有的查詢方法都是原子的梗顺,并且使用了內(nèi)部鎖或者其他形式的并發(fā)控制寺谤。
- ArrayBlockingQueue:基于數(shù)組的先進(jìn)先出隊(duì)列,此隊(duì)列創(chuàng)建必須指定大兴辈ァ变屁;
- LinkedBlockingQueue:基于鏈表的先進(jìn)先出,默認(rèn)大小無限大(注意內(nèi)存溢出)意狠,吞吐量通常高于ArrayBlockingQueue粟关。
- synchronousQueue:不存儲(chǔ)元素的存儲(chǔ)隊(duì)列,每個(gè)插入操作必須等待另一個(gè)線程調(diào)用移除操作环戈,否則該插入操作一直處于阻塞狀態(tài)闷板。吞吐量要高于LinkedBlockingQueue澎灸。
1.6 任務(wù)拒絕策略
線程池的任務(wù)緩存隊(duì)列已滿并且線程池中線程數(shù)量已達(dá)到maxmumPoolSize,如果還有任務(wù)的到來就會(huì)采取拒絕策略遮晚。通常有四種處理策略性昭。
1.6.1 線程池默認(rèn)的處理策略
處理策略 | 效果 |
---|---|
AbortPolicy | 丟棄任務(wù)并拋出RejectedExecutionException異常 |
DiscardPolicy | 也是丟棄任務(wù),但是不拋出異常 |
DiscardOldestPolicy | 丟棄隊(duì)列最前面的任務(wù)县遣,然后重新嘗試執(zhí)行任務(wù)(重復(fù)此過程) |
CallerRunsPolicy | 只要線程池未關(guān)閉糜颠,該策略直接在調(diào)用者線程中串行運(yùn)行被丟棄的任務(wù),顯然這樣不會(huì)真的丟棄任務(wù)萧求,但是可能會(huì)造成調(diào)用者性能急劇下降 |
1.6.2 自定義線程池處理策略
@Slf4j
public class PrintingPolicy implements RejectedExecutionHandler {
@Override
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
log.info(r.toString() + "被拋棄了其兴!");
}
}
有一種場(chǎng)景是:當(dāng)線程池滿了之后,我們的拒絕策略:主線程保持阻塞夸政,線程池有資源了元旬,主線程在將任務(wù)交由線程池處理∈匚剩【不丟棄任務(wù)法绵,當(dāng)線程池沒資源,主線程等待線程池有資源】
因?yàn)槲覀円话懵暶骶€程池酪碘,使用的是ArrayBlockingQueue
有界的阻塞隊(duì)列,而BlockingQueue
特點(diǎn)是當(dāng)隊(duì)列滿了盐茎,線程向隊(duì)列存數(shù)據(jù)兴垦,線程會(huì)被阻塞。
@Slf4j
public class BlockPolicy implements RejectedExecutionHandler {
@Override
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
try {
executor.getQueue().put(r);
} catch (InterruptedException e) {
log.error("", e);
}
}
}
1.7 線程池的關(guān)閉
ThreadPoolExecutor提供了兩個(gè)方法字柠,用于線程池的關(guān)閉探越。
shutdown():不會(huì)立即終止線程池,而是要等所有任務(wù)緩存隊(duì)列中的任務(wù)都執(zhí)行完后才終止窑业,但再也不會(huì)接受新的任務(wù)
shutdownNow():立即終止線程池钦幔,并嘗試打斷正在執(zhí)行的任務(wù),并且清空任務(wù)緩存隊(duì)列常柄,返回尚未執(zhí)行的任務(wù)
1.8 線程池容量的動(dòng)態(tài)調(diào)整
ThreadPoolExcutor提供了動(dòng)態(tài)調(diào)整線程容量大小的方法:
setCorePoolSize:設(shè)置核心池大小鲤氢。
setMaximumPoolSize:設(shè)置線程池最大能創(chuàng)建的線程數(shù)目大小。
1.9 創(chuàng)建線程池的API
Executors類中也提供了幾個(gè)靜態(tài)方法來創(chuàng)建線程池:(不推薦使用)
1. java.util.concurrent.Executors#newCachedThreadPool()
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
corePoolSize大小為0西潘;maxmumPoolSize為無限大卷玉;任務(wù)隊(duì)列為SynchronousQueue,該隊(duì)列沒有緩存區(qū)喷市,即不會(huì)有任務(wù)在該隊(duì)列中排隊(duì)相种。
該線程池時(shí)為了緩存頻繁用到的線程。
因?yàn)槭?strong>"緩存"線程池品姓,沒有緩存可以永久有效寝并,因此coreSizePool為0箫措,因此任務(wù)隊(duì)列的緩存區(qū)應(yīng)該為0。否則即便系統(tǒng)有可用線程資源衬潦,當(dāng)有新的任務(wù)進(jìn)入時(shí)也不會(huì)立即執(zhí)行斤蔓,而是進(jìn)入任務(wù)隊(duì)列排隊(duì)直到隊(duì)列滿,這樣顯然不合理别渔。
由于隊(duì)列緩沖區(qū)為空附迷,那么每來一個(gè)任務(wù),都會(huì)新建線程執(zhí)行任務(wù)哎媚,這樣可能會(huì)導(dǎo)致大量線程被創(chuàng)建喇伯,進(jìn)而系統(tǒng)癱瘓。
2. java.util.concurrent.Executors#newSingleThreadExecutor()
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
單線程化的線程池拨与,它只會(huì)用唯一的工作線程來執(zhí)行任務(wù)稻据。但是需要注意的是大量任務(wù)堆積在LinkedBlockingQueue中,可能會(huì)造成內(nèi)存溢出买喧。
3. java.util.concurrent.Executors#newFixedThreadPool(int)
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
創(chuàng)建定長(zhǎng)線程池捻悯,可控制線程最大并發(fā)數(shù),超過的線程會(huì)在隊(duì)列中等待淤毛。
注意事項(xiàng)
若是線程池采用的是LinkedBlockingQueue今缚,那么若是消費(fèi)的速度小于生產(chǎn)的速度,內(nèi)存隨著時(shí)間的堆積低淡,可能會(huì)造成內(nèi)存溢出姓言。
解決方案:可以采用有界隊(duì)列,可能造成部分任務(wù)被丟棄蔗蹋。