一、簡(jiǎn)介
一種管理線程的技術(shù)嗜闻,可以緩存線程
(1)復(fù)用已經(jīng)存在的線程蜕依,減少線程的創(chuàng)建和銷(xiāo)毀
(2)提高響應(yīng)時(shí)間,已有線程可以直接執(zhí)行任務(wù)琉雳,無(wú)需等待線程創(chuàng)建
(3)方便管理線程數(shù)量样眠,防止過(guò)多的線程占用太多內(nèi)存,同時(shí)能防止過(guò)多的線程切換翠肘,提高CPU效率
二檐束、構(gòu)造函數(shù)
ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler)
int corePoolSize: 核心線程數(shù),在不設(shè)置 allowCoreThreadTimeOut 為 true 的情況下束倍,核心線程就算沒(méi)事做也不會(huì)被銷(xiāo)毀被丧。
int maximumPoolSize : 最大線程數(shù)
long keepAliveTime:超時(shí)時(shí)長(zhǎng),一個(gè)非核心線程(設(shè)置 allowCoreThreadTimeOut 為 true 也同樣作用于核心線程)在處于閑置狀態(tài)(沒(méi)事做)超過(guò)這個(gè)時(shí)長(zhǎng)就 會(huì)被銷(xiāo)毀绪妹。
TimeUnit unit:時(shí)間單位甥桂,秒、毫秒邮旷、微秒等
BlockingQueue<Runnable> workQueue: 阻塞隊(duì)列
ThreadFactory threadFactory:線程池工廠黄选,可以自己實(shí)現(xiàn),方便設(shè)置線程的名字以及優(yōu)先級(jí)
RejectedExecutionHandler handler:拒絕策略
三婶肩、常見(jiàn)的線程池
CachedThreadPool
可緩存線程池
Executors.newCachedThreadPool(ThreadFactory threadFactory) {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>(),
threadFactory);
}
1. 內(nèi)部沒(méi)有核心線程办陷,線程的數(shù)量是有沒(méi)限制的貌夕。
2. 在創(chuàng)建任務(wù)時(shí),若有空閑的線程時(shí)則復(fù)用空閑的線程懂诗,若沒(méi)有則新建線程蜂嗽。
3. 沒(méi)有工作的線程(閑置狀態(tài))在超過(guò)了60S沒(méi)有任務(wù)苗膝,就會(huì)銷(xiāo)毀殃恒。
FixedThreadPool
定長(zhǎng)線程池
Executors.newFixedThreadPool(int nThreads, ThreadFactory threadFactory) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>(),
threadFactory);
}
1. 最大線程數(shù)等于核心線程數(shù),所以在默認(rèn)情況下辱揭,該線程池的線程不會(huì)因?yàn)殚e置狀態(tài)超時(shí)而被銷(xiāo)毀离唐。
2. 如果當(dāng)前線程數(shù)小于核心線程數(shù),并且也有閑置線程的時(shí)候提交了任務(wù)问窃,這時(shí)也不會(huì)去復(fù)用之前的閑置線程亥鬓,會(huì)創(chuàng)建新的線程去執(zhí)行任務(wù)。如果當(dāng)前執(zhí)行任務(wù)數(shù)大于了核心線程數(shù)域庇,大于的部分就會(huì)進(jìn)入隊(duì)列等待嵌戈。等著有閑置的線程來(lái)執(zhí)行這個(gè)任務(wù)。LinkedBlockingQueue默認(rèn)大小是Integer.MAX_VALUE听皿,可能導(dǎo)致內(nèi)存占用過(guò)多
ScheduledThreadPool
定時(shí)任務(wù)線程池
public static ScheduledExecutorService newScheduledThreadPool(
int corePoolSize, ThreadFactory threadFactory) {
return new ScheduledThreadPoolExecutor(corePoolSize, threadFactory);
}
public ScheduledThreadPoolExecutor(int corePoolSize,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) {
super(corePoolSize, Integer.MAX_VALUE,
DEFAULT_KEEPALIVE_MILLIS, MILLISECONDS,
new DelayedWorkQueue(), threadFactory, handler);
}
1. 設(shè)置了核心線程數(shù)熟呛,最大線程數(shù)也是 Integer.MAX_VALUE。
2. 這個(gè)線程池是上述4個(gè)中為唯一個(gè)有延遲執(zhí)行和周期執(zhí)行任務(wù)的線程池尉姨。
SingleThreadExecutor
單線程池
public static ExecutorService newSingleThreadExecutor(ThreadFactory threadFactory) {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>(),
threadFactory));
}
1庵朝、只有一個(gè)線程且不回收
2、LinkedBlockingQueue默認(rèn)大小是Integer.MAX_VALUE又厉,可能導(dǎo)致內(nèi)存占用過(guò)多
四九府、阻塞隊(duì)列
常用的幾種隊(duì)列
(1)ArrayBlockingQueue:基于數(shù)組實(shí)現(xiàn),固定大小覆致,其構(gòu)造必須指定大小侄旬。其所含的對(duì)象是FIFO順序排序的。
(2)LinkedBlockingQueue:基于鏈表實(shí)現(xiàn)煌妈,默認(rèn)大小Integer.MAX_VALUE勾怒,可傳入。其所含的對(duì)象是FIFO順序排序的声旺。
(3)PriorityBlockingQueue:類(lèi)似于LinkedBlockingQueue笔链,但是其所含對(duì)象的排序不是FIFO,而是依據(jù)對(duì)象的自然順序或者構(gòu)造函數(shù)的Comparator決定腮猖。無(wú)界隊(duì)列
(4)SynchronousQueue:不存儲(chǔ)元素的阻塞隊(duì)列鉴扫,大小為0,每一個(gè)put都必須等待take操作澈缺,提供任務(wù)的轉(zhuǎn)發(fā)坪创。
(5) DelayQueue:基于PriorityQueue炕婶, 支持延時(shí)獲取元素, 無(wú)界
常用方法
拋異常 | 返回特殊值 | 一直阻塞 | 超時(shí)退出 | |
---|---|---|---|---|
插入數(shù)據(jù) | add(e) | offer(e) | put(e) | offer(e, time, unit) |
移除數(shù)據(jù) | remove(e) | poll() | take() | poll(time, unit) |
take:
public E take() throws InterruptedException {
final ReentrantLock lock = this.lock;
lock.lockInterruptibly();
try {
while (count == 0)
notEmpty.await();
return dequeue();
} finally {
lock.unlock();
}
}
五莱预、拒絕策略
1柠掂、拒絕時(shí)機(jī)
線程池沒(méi)有能力處理新任務(wù):最大線程數(shù)大于且隊(duì)列數(shù)量到達(dá)了最大值
線程池被調(diào)用了shutdown關(guān)閉之后,還沒(méi)有完全停止的時(shí)候依沮。
2涯贞、策略
AbortPolicy:默認(rèn)的策略,直接拋出RejectedExecutionException ,RuntimeException
DiscardPolicy:默默丟棄任務(wù)危喉,不進(jìn)行任何通知宋渔。實(shí)際上內(nèi)部啥也沒(méi)干
DiscardOldestPolicy:丟棄掉在隊(duì)列中存在時(shí)間最久的任務(wù),然后重新提交任務(wù)
CallerRunsPolicy:讓提交任務(wù)的線程去執(zhí)行任務(wù)辜限,不會(huì)丟失業(yè)務(wù)數(shù)據(jù)皇拣,阻塞了提交任務(wù)的線程,減緩提交任務(wù)的速度
3薄嫡、拒絕策略使用
關(guān)鍵業(yè)務(wù)要自定義拒絕策略氧急,可以記錄日志,也可以把拒絕結(jié)果返回給用戶
六毫深、合理使用線程池
java 開(kāi)發(fā)手冊(cè)<并發(fā)處理>中片段:
[圖片上傳失敗...(image-cdc18e-1600494356170)]
1吩坝、自定義線程池時(shí),使用自定義ThreadFactory费什,指定線程名钾恢,方便排查問(wèn)題
2、分析業(yè)務(wù)數(shù)量級(jí)鸳址,如果會(huì)很大瘩蚪,必須自定義線程池,限定最大線程池稿黍,使用有界隊(duì)列疹瘦,防止jvm OOM
3、即使限制了最大線程數(shù)巡球,過(guò)多的線程會(huì)導(dǎo)致線程切換頻繁言沐,cpu效率地下,也要考慮為別的業(yè)務(wù)讓出cpu
4酣栈、核心線程數(shù)险胰,看任務(wù),如果時(shí)一天執(zhí)行一次矿筝,設(shè)置為0起便,跑完釋放內(nèi)存。如果常用,看任務(wù)量
5榆综、拒絕策略要重寫(xiě)妙痹,默認(rèn)的策略AbortPolicy會(huì)導(dǎo)致crash
七、線程池的復(fù)用原理
1鼻疮、回顧線程創(chuàng)建
new Thread(new Runnable() {
@Override
public void run() {
}
}).start();
創(chuàng)建一個(gè)線程怯伊,調(diào)用start()啟動(dòng)線程,線程執(zhí)行完run方法判沟,就進(jìn)入Dead狀態(tài)耿芹。start()方法只能調(diào)用一次
2、線程池提交任務(wù)
ExecutorService executorService = Executors.newFixedThreadPool(2);
executorService.submit(new Runnable() {
@Override
public void run() {
}
});
3水评、復(fù)用原理
復(fù)用線程的本質(zhì):提交任務(wù)猩系,在任務(wù)內(nèi)死循環(huán)執(zhí)行提交下來(lái)的任務(wù)
public void run() {
while (true) {
if(tasks available) {
Runnable task = taskqueue.dequeue();
task.run();
} else{
// wait or shutdown
}
}
}
線程創(chuàng)建執(zhí)行任務(wù)流程:
(1) execute提交任務(wù)
判斷當(dāng)前工作線程數(shù)是否小于核心線程數(shù)媚送,需要?jiǎng)?chuàng)建新線程則進(jìn)入addWorker
(2) addWorker 創(chuàng)建線程
private final class Worker implements Runnable{
final Thread thread;
Runnable firstTask;
Worker(Runnable firstTask) {
this.firstTask = firstTask;
// 把 Worker 作為 thread 運(yùn)行的任務(wù)
this.thread = getThreadFactory().newThread(this);
}
public void run() {
runWorker(this);
}
}
(3)創(chuàng)建Worker
Worker以提交下來(lái)的任務(wù)作為構(gòu)造參數(shù)中燥,并創(chuàng)建一個(gè)新的線程
w = new Worker(firstTask);
final Thread t = w.thread;
workers.add(w);
(4)runWorker
調(diào)用Worker內(nèi)部變量thread的start()方法啟動(dòng)線程
final Thread t = worker.thread;
t.start();
線程啟動(dòng)后,調(diào)用到Worker內(nèi)部的run方法
public void run() {
runWorker(this);
}
(5)循環(huán)執(zhí)行任務(wù)
final void runWorker(Worker w) {
Runnable task = w.firstTask;
while (task != null || (task = getTask()) != null) {
try {
task.run();
} finally {
task = null;
}
}
}
Runnable r = timed ?
workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
workQueue.take()
八塘偎、關(guān)閉線程池
shutdown() : 調(diào)用之后疗涉,線程池不是立刻就關(guān)閉,等待任務(wù)隊(duì)列中的任務(wù)執(zhí)行完才徹底關(guān)閉吟秩。同時(shí)咱扣,還會(huì)根據(jù)拒絕策略拒絕新提交的任務(wù)
isShutdown(): 返回線程池是否已經(jīng)開(kāi)始了關(guān)閉工作,返回不代表已經(jīng)徹底關(guān)閉涵防。
isTerminated(): 檢測(cè)線程池是否真正的關(guān)閉了
shutdownNow(): 強(qiáng)制關(guān)閉線程池闹伪,給所有線程發(fā)送interrupt中斷信號(hào),然后所有的任務(wù)轉(zhuǎn)移到另外一個(gè)List中壮池。方便調(diào)用這做一些補(bǔ)救操作偏瓤。
參考文檔
Java 阻塞隊(duì)列--BlockingQueue: https://www.cnblogs.com/bjxq-cs88/p/9759571.html
Java 線程池中的線程復(fù)用是如何實(shí)現(xiàn):http://www.reibang.com/p/f84a61917b03
Tomcat 高并發(fā)之道原理拆解與性能調(diào)優(yōu):https://mp.weixin.qq.com/s/4_En4hHeolYyO0RcliGxCQ
Java線程池的拒絕策略:https://www.cnblogs.com/eric-fang/p/11584142.html