更多精彩請(qǐng)關(guān)注公眾號(hào)xhJaver,京東java工程師和你一起成長(zhǎng)
我們知道,在計(jì)算機(jī)中創(chuàng)建一個(gè)線程和銷(xiāo)毀一個(gè)線程都是十分耗費(fèi)資源的操作节榜,有一種思想叫做羡玛,池化思想,就是說(shuō)我們創(chuàng)建個(gè)池子宗苍,把耗費(fèi)資源的操作都提前做好稼稿,后面大家一起用創(chuàng)建好的東西,最后統(tǒng)一銷(xiāo)毀讳窟。省去了用一次創(chuàng)建一次让歼,銷(xiāo)毀一次,這種耗費(fèi)資源的操作挪钓。
一是越、線程池工作原理
線程池就是這種思想耳舅,他的基本工作流程如下圖所示
那么他的核心線程碌上,任務(wù)隊(duì)列倚评,這些又是什么呢?怎么設(shè)置呢馏予?
這些就要從代碼入手了天梧,我們先來(lái)看下線程池構(gòu)造方法的代碼
二、線程池構(gòu)造方法
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.acc = System.getSecurityManager() == null ?
null :
AccessController.getContext();
this.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.workQueue = workQueue;
this.keepAliveTime = unit.toNanos(keepAliveTime);
this.threadFactory = threadFactory;
this.handler = handler;
}
其實(shí)ThreadPoolExecutor有四種構(gòu)造方法霞丧,不過(guò)底層都是用這個(gè)7個(gè)參數(shù)的構(gòu)造方法呢岗,所以我們弄懂這一個(gè)就好了,以下是其他構(gòu)造方法的底層實(shí)現(xiàn)
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
RejectedExecutionHandler handler) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), handler);
}
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), defaultHandler);
}
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
threadFactory, defaultHandler);
}
其中默認(rèn)的拒絕策略是
private static final RejectedExecutionHandler defaultHandler =
new AbortPolicy();
這些構(gòu)造方法中的
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
threadFactory, defaultHandler);
就是那七個(gè)參數(shù)的構(gòu)造方法
有點(diǎn)懵蛹尝?沒(méi)關(guān)系后豫,接下來(lái)我們一個(gè)個(gè)的解析這七個(gè)參數(shù)的意思
三、線程池參數(shù)介紹
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.acc = System.getSecurityManager() == null ?
null :
AccessController.getContext();
this.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.workQueue = workQueue;
this.keepAliveTime = unit.toNanos(keepAliveTime);
this.threadFactory = threadFactory;
this.handler = handler;
}
1. 第一個(gè)參數(shù) corePoolSize 代表這個(gè)線程池的核心線程數(shù)
2. 第二個(gè)參數(shù) maximumPoolSize 代表這個(gè)線程池的最大線程數(shù) (核心線程數(shù) +非核心線程數(shù))
3. 第三個(gè)參數(shù) keepAliveTime 代表這個(gè)線程池的非核心線程的空閑時(shí)的存活時(shí)間
4. 第四個(gè)參數(shù) unit 代表這個(gè)線程池的非核心線程的空閑存活時(shí)間的單位
5. 第五個(gè)參數(shù) workQueue 代表這個(gè)線程池的任務(wù)阻塞隊(duì)列突那,jdk中有幾種常見(jiàn)的阻塞隊(duì)列
- ArrayBlockingQueue:基于數(shù)組結(jié)構(gòu)的有界阻塞隊(duì)列
- LinkedBlockingQueue:是一個(gè)基于鏈表結(jié)構(gòu)的阻塞隊(duì)列
- SynchronousQueue :同步隊(duì)列挫酿,只存儲(chǔ)一個(gè)任務(wù),插入任務(wù)時(shí)要等待(如果隊(duì)列里有元素的話)取出任務(wù)時(shí)要等待(如果隊(duì)列里沒(méi)有元素的話)
- PriorityBlockingQueue:優(yōu)先級(jí)隊(duì)列愕难,進(jìn)入隊(duì)列的元素按照優(yōu)先級(jí)會(huì)進(jìn)行排序
建議:建議使用有界隊(duì)列早龟,要是無(wú)界隊(duì)列的話,任務(wù)太多的話可能會(huì)導(dǎo)致OOM
6. 第六個(gè)參數(shù) threadFactory(可以自定義) 代表這個(gè)線程池的創(chuàng)建線程的工廠猫缭,有兩種
- Executors.privilegedThreadFactory() 使用訪問(wèn)權(quán)限創(chuàng)建一個(gè)權(quán)限控制的線程葱弟。
- Executors.defaultThreadFactory() 將創(chuàng)建一個(gè)同線程組且默認(rèn)優(yōu)先級(jí)的線程
7. 第七個(gè)參數(shù) handler(可以自定義) 代表這個(gè)線程池的拒絕處理任務(wù)的飽和策略,jdk默認(rèn)提供了四種
- new ThreadPoolExecutor.AbortPolicy(); 直接拋出異常
- new ThreadPoolExecutor.CallerRunsPolicy(); 用當(dāng)前調(diào)用者的線程中處理傳過(guò)來(lái)的任務(wù)
- new ThreadPoolExecutor.DiscardOldestPolicy(); 丟棄最老的一個(gè)任務(wù)猜丹,然后把傳過(guò)來(lái)的任務(wù)加入到阻塞隊(duì)列中
- new ThreadPoolExecutor.DiscardPolicy(); 什么都不做芝加,直接丟掉傳過(guò)來(lái)的任務(wù)
四、使用線程池例子
基礎(chǔ)概念也都看了射窒,下面來(lái)看個(gè)使用線程池處理任務(wù)的小例子
首先妖混,我們先創(chuàng)建個(gè)任務(wù)類(lèi)
public class Task implements Runnable {
private String taskName;
public Task(String taskName) {
this.taskName = taskName;
}
@Override
public void run() {
try {
//模擬每個(gè)任務(wù)的耗時(shí)
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
String name = Thread.currentThread().getName();
System.out.println("這里是xhJaver,線程池系列 當(dāng)前線程名字是 " + name+" 處理了 "+ taskName+" 任務(wù)");
}
}
我們?cè)賮?lái)看測(cè)試類(lèi)
public class Demo1 {
public static void main(String[] args) {
//阻塞隊(duì)列轮洋,設(shè)置阻塞任務(wù)最多為10個(gè)
BlockingQueue<Runnable> blockingQueue = new ArrayBlockingQueue<Runnable> (10);
//線程工廠
ThreadFactory threadFactory = Executors.defaultThreadFactory();
//拒絕策略 當(dāng)線程池的最大工作線程跑滿以及阻塞隊(duì)列滿了的話制市,會(huì)由拒絕策略處理剩下的任務(wù)
ThreadPoolExecutor.AbortPolicy abortPolicy = new ThreadPoolExecutor.AbortPolicy();
//創(chuàng)建線程池 核心線程數(shù)為5 最大線程數(shù)為10 非核心線程空閑存活時(shí)間為60s
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(5, 10, 60L,
TimeUnit.SECONDS, blockingQueue, threadFactory, abortPolicy
);
for (int i=0;i<10;i++){
//創(chuàng)建10個(gè)任務(wù),如果要是創(chuàng)建>20個(gè)任務(wù)弊予,則20以外的任務(wù)會(huì)交由拒絕策略處理
Task task = new Task("task" + i);
//讓我們自定義的線程池去跑這些任務(wù)
threadPoolExecutor.execute(task);
}
//記得要關(guān)閉線程池
threadPoolExecutor.shutdown();
}
}
輸出結(jié)果是
這里是xhJaver祥楣,線程池系列 當(dāng)前線程名字是 pool-1-thread-1 處理了 task0 任務(wù)
這里是xhJaver,線程池系列 當(dāng)前線程名字是 pool-1-thread-2 處理了 task1 任務(wù)
這里是xhJaver汉柒,線程池系列 當(dāng)前線程名字是 pool-1-thread-3 處理了 task2 任務(wù)
這里是xhJaver误褪,線程池系列 當(dāng)前線程名字是 pool-1-thread-4 處理了 task3 任務(wù)
這里是xhJaver,線程池系列 當(dāng)前線程名字是 pool-1-thread-5 處理了 task4 任務(wù)
這里是xhJaver碾褂,線程池系列 當(dāng)前線程名字是 pool-1-thread-1 處理了 task5 任務(wù)
這里是xhJaver兽间,線程池系列 當(dāng)前線程名字是 pool-1-thread-2 處理了 task6 任務(wù)
這里是xhJaver,線程池系列 當(dāng)前線程名字是 pool-1-thread-5 處理了 task9 任務(wù)
這里是xhJaver正塌,線程池系列 當(dāng)前線程名字是 pool-1-thread-4 處理了 task8 任務(wù)
這里是xhJaver嘀略,線程池系列 當(dāng)前線程名字是 pool-1-thread-3 處理了 task7 任務(wù)
五恤溶、線程工廠是什么 ?
文中一直說(shuō)線程工廠線程工廠帜羊,這線程工場(chǎng)到底是干嘛的呢咒程? 當(dāng)然是創(chuàng)建線程的工廠啦,創(chuàng)建線程讼育,線程當(dāng)然得有個(gè)名字咯帐姻,就像剛才的小例子輸出的一樣,線程的名字是pool-1-thread-3等等奶段,我現(xiàn)在不想叫這個(gè)名字了饥瓷,那就叫thread-xhJaver吧,這是自定義的名字痹籍,那怎么自定義呢?
首先扛伍,要實(shí)現(xiàn)ThreadFactory接口中的Thread newThread(Runnable r)方法, 傳入一個(gè)任務(wù)词裤,返回一個(gè)自定義線程刺洒,如下面的代碼一樣
public class DIYThreadFactory implements ThreadFactory {
private AtomicInteger atomicInteger;
public DIYThreadFactory( AtomicInteger atomicInteger){
this.atomicInteger = atomicInteger;
}
@Override
public Thread newThread(Runnable r) {
Thread thread = new Thread(r);
thread.setName("xhJaver-thread-"+atomicInteger.getAndIncrement());
return thread;
}
}
然后在使用時(shí)傳入這個(gè)自定義的線程工廠
public static void main(String[] args) {
//阻塞隊(duì)列,設(shè)置阻塞任務(wù)最多為10個(gè)
BlockingQueue<Runnable> blockingQueue = new ArrayBlockingQueue<Runnable> (10);
//創(chuàng)建線程安全的計(jì)數(shù)器
AtomicInteger atomicInteger = new AtomicInteger();
//自定義線程工廠
ThreadFactory threadFactory = new DIYThreadFactory(atomicInteger);
//拒絕策略 當(dāng)線程池的最大工作線程跑滿以及阻塞隊(duì)列滿了的話吼砂,會(huì)由拒絕策略處理剩下的任務(wù)
ThreadPoolExecutor.AbortPolicy abortPolicy = new ThreadPoolExecutor.AbortPolicy();
//創(chuàng)建線程池 核心線程數(shù)為5 最大線程數(shù)為10 非核心線程空閑存活時(shí)間為60s
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(5, 10, 60L,
TimeUnit.SECONDS, blockingQueue, threadFactory, abortPolicy
);
for (int i=0;i<10;i++){
//創(chuàng)建10個(gè)任務(wù)逆航,如果要是創(chuàng)建>20個(gè)任務(wù),則20以外的任務(wù)會(huì)交由拒絕策略處理
Task task = new Task("task" + i);
//讓我們自定義的線程池去跑這些任務(wù)
threadPoolExecutor.execute(task);
}
//記得要關(guān)閉線程池
threadPoolExecutor.shutdown();
}
輸出結(jié)果是
這里是xhJaver渔肩,線程池系列 當(dāng)前線程名字是 xhJaver-thread-0 處理了 task0 任務(wù)
這里是xhJaver因俐,線程池系列 當(dāng)前線程名字是 xhJaver-thread-1 處理了 task1 任務(wù)
這里是xhJaver,線程池系列 當(dāng)前線程名字是 xhJaver-thread-4 處理了 task4 任務(wù)
這里是xhJaver周偎,線程池系列 當(dāng)前線程名字是 xhJaver-thread-3 處理了 task3 任務(wù)
這里是xhJaver抹剩,線程池系列 當(dāng)前線程名字是 xhJaver-thread-2 處理了 task2 任務(wù)
這里是xhJaver,線程池系列 當(dāng)前線程名字是 xhJaver-thread-0 處理了 task5 任務(wù)
這里是xhJaver蓉坎,線程池系列 當(dāng)前線程名字是 xhJaver-thread-1 處理了 task6 任務(wù)
這里是xhJaver澳眷,線程池系列 當(dāng)前線程名字是 xhJaver-thread-2 處理了 task9 任務(wù)
這里是xhJaver,線程池系列 當(dāng)前線程名字是 xhJaver-thread-3 處理了 task8 任務(wù)
這里是xhJaver蛉艾,線程池系列 當(dāng)前線程名字是 xhJaver-thread-4 處理了 task7 任務(wù)
我也學(xué)會(huì)了自定義線程工廠了钳踊,可自定義名字到底有用呢,當(dāng)然是排查問(wèn)題拔鸷睢拓瞪!把線程名字定義為和自己業(yè)務(wù)有關(guān)的名字,到時(shí)候報(bào)錯(cuò)的時(shí)候就方便排查了助琐。
六祭埂、 拒絕策略是什么
線程工廠可以自定義,那拒絕策略可以自定義嗎兵钮?當(dāng)然可以啦 方法如下蛆橡,首先也要實(shí)現(xiàn)一個(gè)RejectedExecutionHandler接口舌界,重寫(xiě)rejectedExecution 這個(gè)方法
public class DIYRejectedHandler implements RejectedExecutionHandler {
@Override
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
//記錄日志等操作
System.out.println("這是xhJaver無(wú)法處理的任務(wù) "+r.toString()+" 當(dāng)前線程名字是 "+Thread.currentThread().getName());
}
}
然后在使用時(shí)傳入這個(gè)自定義的拒絕策略
public static void main(String[] args) {
//阻塞隊(duì)列,設(shè)置阻塞任務(wù)最多為10個(gè)
BlockingQueue<Runnable> blockingQueue = new ArrayBlockingQueue<Runnable> (10);
//創(chuàng)建線程安全的計(jì)數(shù)器
AtomicInteger atomicInteger = new AtomicInteger();
//自定義線程工廠
ThreadFactory threadFactory = new DIYThreadFactory(atomicInteger);
//自定義拒絕策略 當(dāng)線程池的最大工作線程跑滿以及阻塞隊(duì)列滿了的話航罗,會(huì)由拒絕策略處理剩下的任務(wù)
DIYRejectedHandler diyRejectedHandler = new DIYRejectedHandler();
//創(chuàng)建線程池 核心線程數(shù)為5 最大線程數(shù)為10 非核心線程空閑存活時(shí)間為60s
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(5, 10, 60L,
TimeUnit.SECONDS, blockingQueue, threadFactory, diyRejectedHandler
);
for (int i=0;i<30;i++){
//創(chuàng)建10個(gè)任務(wù),如果要是創(chuàng)建>20個(gè)任務(wù)屁药,則20以外的任務(wù)會(huì)交由拒絕策略處理
Task task = new Task("task" + i);
//讓我們自定義的線程池去跑這些任務(wù)
threadPoolExecutor.execute(task);
}
//記得要關(guān)閉線程池
threadPoolExecutor.shutdown();
}
輸出結(jié)果是
這是xhJaver無(wú)法處理的任務(wù) Task{taskName='task20'} 當(dāng)前線程名字是 main
這是xhJaver無(wú)法處理的任務(wù) Task{taskName='task21'} 當(dāng)前線程名字是 main
這是xhJaver無(wú)法處理的任務(wù) Task{taskName='task22'} 當(dāng)前線程名字是 main
這是xhJaver無(wú)法處理的任務(wù) Task{taskName='task23'} 當(dāng)前線程名字是 main
這是xhJaver無(wú)法處理的任務(wù) Task{taskName='task24'} 當(dāng)前線程名字是 main
這是xhJaver無(wú)法處理的任務(wù) Task{taskName='task25'} 當(dāng)前線程名字是 main
這是xhJaver無(wú)法處理的任務(wù) Task{taskName='task26'} 當(dāng)前線程名字是 main
這是xhJaver無(wú)法處理的任務(wù) Task{taskName='task27'} 當(dāng)前線程名字是 main
這是xhJaver無(wú)法處理的任務(wù) Task{taskName='task28'} 當(dāng)前線程名字是 main
這是xhJaver無(wú)法處理的任務(wù) Task{taskName='task29'} 當(dāng)前線程名字是 main
這里是xhJaver粥血,線程池系列 當(dāng)前線程名字是 xhJaver-thread-5 處理了 task15 任務(wù)
這里是xhJaver,線程池系列 當(dāng)前線程名字是 xhJaver-thread-4 處理了 task4 任務(wù)
這里是xhJaver酿箭,線程池系列 當(dāng)前線程名字是 xhJaver-thread-3 處理了 task3 任務(wù)
這里是xhJaver复亏,線程池系列 當(dāng)前線程名字是 xhJaver-thread-2 處理了 task2 任務(wù)
這里是xhJaver,線程池系列 當(dāng)前線程名字是 xhJaver-thread-1 處理了 task1 任務(wù)
這里是xhJaver缭嫡,線程池系列 當(dāng)前線程名字是 xhJaver-thread-0 處理了 task0 任務(wù)
這里是xhJaver缔御,線程池系列 當(dāng)前線程名字是 xhJaver-thread-9 處理了 task19 任務(wù)
這里是xhJaver,線程池系列 當(dāng)前線程名字是 xhJaver-thread-8 處理了 task18 任務(wù)
這里是xhJaver妇蛀,線程池系列 當(dāng)前線程名字是 xhJaver-thread-7 處理了 task17 任務(wù)
這里是xhJaver耕突,線程池系列 當(dāng)前線程名字是 xhJaver-thread-6 處理了 task16 任務(wù)
這里是xhJaver,線程池系列 當(dāng)前線程名字是 xhJaver-thread-4 處理了 task6 任務(wù)
這里是xhJaver评架,線程池系列 當(dāng)前線程名字是 xhJaver-thread-5 處理了 task5 任務(wù)
這里是xhJaver眷茁,線程池系列 當(dāng)前線程名字是 xhJaver-thread-3 處理了 task7 任務(wù)
這里是xhJaver,線程池系列 當(dāng)前線程名字是 xhJaver-thread-2 處理了 task8 任務(wù)
這里是xhJaver纵诞,線程池系列 當(dāng)前線程名字是 xhJaver-thread-1 處理了 task9 任務(wù)
這里是xhJaver上祈,線程池系列 當(dāng)前線程名字是 xhJaver-thread-0 處理了 task10 任務(wù)
這里是xhJaver,線程池系列 當(dāng)前線程名字是 xhJaver-thread-9 處理了 task11 任務(wù)
這里是xhJaver浙芙,線程池系列 當(dāng)前線程名字是 xhJaver-thread-8 處理了 task12 任務(wù)
這里是xhJaver登刺,線程池系列 當(dāng)前線程名字是 xhJaver-thread-7 處理了 task13 任務(wù)
這里是xhJaver,線程池系列 當(dāng)前線程名字是 xhJaver-thread-6 處理了 task14 任務(wù)
七嗡呼、常見(jiàn)的阻塞隊(duì)列及注意點(diǎn)
因?yàn)樽枞?duì)列的知識(shí)太多了纸俭,后續(xù)我們會(huì)單獨(dú)開(kāi)篇來(lái)講這個(gè)阻塞隊(duì)列,先介紹幾個(gè)常用的
1.ArrayBlockingQueue 基于數(shù)組的有界隊(duì)列
2.LinkedBlockingQueue 基于鏈表的無(wú)界隊(duì)列
3.SynchronousQueue
它內(nèi)部只有一個(gè)元素南窗,插入時(shí)如果發(fā)現(xiàn)內(nèi)部有元素未被取走則阻塞掉蔬,取元素時(shí)若隊(duì)列沒(méi)有元素則被阻 塞,直到有元素插入進(jìn)來(lái)矾瘾。
搭配線程池使用如下 女轿,先創(chuàng)建任務(wù)類(lèi)
public class Task implements Runnable {
private String taskName;
public Task(String taskName) {
this.taskName = taskName;
}
@Override
public String toString() {
return "Task{" +
"taskName='" + taskName + '\'' +
'}';
}
@Override
public void run() {
try {
//模擬每個(gè)任務(wù)的耗時(shí)
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
String name = Thread.currentThread().getName();
System.out.println("這里是xhJaver,線程池系列 當(dāng)前線程名字是 " + name+" 處理了 "+ taskName+" 任務(wù)");
}
}
再使用阻塞隊(duì)列
public static void main(String[] args) {
ExecutorService executorService = Executors.newCachedThreadPool();
for (int i=0;i<10;i++){
//創(chuàng)建十個(gè)任務(wù)
Task task = new Task("task" + i);
//去跑任務(wù)
executorService.execute(task);
}
//記得要關(guān)閉線程池
executorService.shutdown();
}
其中newCachedThreadPool底層就使用的是SynchronousQueue
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
輸出結(jié)果是
這里是xhJaver壕翩,線程池系列 當(dāng)前線程名字是 pool-1-thread-1 處理了 task0 任務(wù)
這里是xhJaver蛉迹,線程池系列 當(dāng)前線程名字是 pool-1-thread-2 處理了 task1 任務(wù)
這里是xhJaver,線程池系列 當(dāng)前線程名字是 pool-1-thread-5 處理了 task4 任務(wù)
這里是xhJaver放妈,線程池系列 當(dāng)前線程名字是 pool-1-thread-4 處理了 task3 任務(wù)
這里是xhJaver北救,線程池系列 當(dāng)前線程名字是 pool-1-thread-3 處理了 task2 任務(wù)
這里是xhJaver荐操,線程池系列 當(dāng)前線程名字是 pool-1-thread-6 處理了 task5 任務(wù)
這里是xhJaver,線程池系列 當(dāng)前線程名字是 pool-1-thread-7 處理了 task6 任務(wù)
這里是xhJaver珍策,線程池系列 當(dāng)前線程名字是 pool-1-thread-10 處理了 task9 任務(wù)
這里是xhJaver托启,線程池系列 當(dāng)前線程名字是 pool-1-thread-9 處理了 task8 任務(wù)
這里是xhJaver,線程池系列 當(dāng)前線程名字是 pool-1-thread-8 處理了 task7 任務(wù)
由此可見(jiàn)攘宙,線程池分別創(chuàng)建了十個(gè)線程來(lái)處理這十個(gè)任務(wù)屯耸,為什么呢? 這是因?yàn)椴渑颐總€(gè)任務(wù)的模擬處理時(shí)間是1s疗绣,當(dāng)再來(lái)的任務(wù)發(fā)現(xiàn)阻塞隊(duì)列中有任務(wù)還沒(méi)被取走,就創(chuàng)建非核心線程處理剛來(lái)的這個(gè)任務(wù)铺韧,不斷的來(lái)任務(wù)多矮,不斷的創(chuàng)建線程,所以用這個(gè)阻塞隊(duì)列再搭配線程池的總線程數(shù)等參數(shù)設(shè)置可能會(huì)因?yàn)椴粩嗟膭?chuàng)建線程而導(dǎo)致OOM哈打。
4.PriorityBlockingQueue 優(yōu)先級(jí)隊(duì)列 進(jìn)入隊(duì)列的元素會(huì)按照任務(wù)的優(yōu)先級(jí)排序塔逃。并且必須實(shí)現(xiàn)Comparable接口。
參數(shù):priorityTask - 要比較的對(duì)象料仗。 返回:負(fù)整數(shù)患雏、零或正整數(shù), 根據(jù)此對(duì)象是小于罢维、等于還是大于指定對(duì)象(要比較的對(duì)象)淹仑。
先創(chuàng)建一個(gè)帶有優(yōu)先級(jí)的任務(wù)
public class PriorityTask implements Runnable , Comparable<PriorityTask>{
private String taskName;
// 優(yōu)先級(jí),根據(jù)這個(gè)數(shù)進(jìn)行排序
private Integer priority;
public PriorityTask(Integer priority,String taskName) {
this.priority = priority;
this.taskName = taskName;
}
//這個(gè)compareTo方法的返回值如果是-1的話肺孵,則排序會(huì)認(rèn)為傳過(guò)來(lái)的任務(wù)比此任務(wù)的大珊佣,降序排列
//這個(gè)compareTo方法的返回值如果是1的話患久,則排序會(huì)認(rèn)為傳過(guò)來(lái)的任務(wù)比此任務(wù)的小,升序排列
@Override
public int compareTo(PriorityTask priorityTask) {
//Integer.compare返回 -1代表 傳過(guò)來(lái)的任務(wù)的priority 比次任務(wù)的priority要小
// Integer.compare 0 傳過(guò)來(lái)的任務(wù)的priority 比次任務(wù)的priority一樣大
//Integer.compare 1 傳過(guò)來(lái)的任務(wù)的priority 比次任務(wù)的priority要大
return Integer.compare(priorityTask.priority,this.priority);
}
@Override
public void run() {
try {
//模擬每個(gè)任務(wù)的耗時(shí)
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
String name = Thread.currentThread().getName();
System.out.println("這里是xhJaver,線程池系列 當(dāng)前線程名字是 " + name+" 處理了 "+ taskName+" 任務(wù)");
}
@Override
public String toString() {
return "Task{" +
"taskName='" + taskName + '\'' +
'}';
}
}
Integer.compare 的 比較大小代碼
java
public static int compare(int x, int y) {
return (x < y) ? -1 : ((x == y) ? 0 : 1);
}
測(cè)試代碼
public static void main(String[] args) {
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(1, 1,
60L, TimeUnit.SECONDS,
new PriorityBlockingQueue());
for (int i=0;i<5;i++){
//創(chuàng)建十個(gè)任務(wù)
PriorityTask priorityTask = new PriorityTask(i,"task" + i);
//去跑任務(wù)
threadPoolExecutor.execute(priorityTask);
}
for (int i=100;i>=95;i--){
//創(chuàng)建十個(gè)任務(wù)
PriorityTask priorityTask = new PriorityTask(i,"task" + i);
//去跑任務(wù)
threadPoolExecutor.execute(priorityTask);
}
//記得要關(guān)閉線程池
threadPoolExecutor.shutdown();
}
輸出結(jié)果是
這里是xhJaver名秀,線程池系列 當(dāng)前線程名字是 pool-1-thread-1 處理了 task0 任務(wù)
這里是xhJaver套腹,線程池系列 當(dāng)前線程名字是 pool-1-thread-1 處理了 task100 任務(wù)
這里是xhJaver窘行,線程池系列 當(dāng)前線程名字是 pool-1-thread-1 處理了 task99 任務(wù)
這里是xhJaver僧须,線程池系列 當(dāng)前線程名字是 pool-1-thread-1 處理了 task98 任務(wù)
這里是xhJaver,線程池系列 當(dāng)前線程名字是 pool-1-thread-1 處理了 task97 任務(wù)
這里是xhJaver紫新,線程池系列 當(dāng)前線程名字是 pool-1-thread-1 處理了 task96 任務(wù)
這里是xhJaver均蜜,線程池系列 當(dāng)前線程名字是 pool-1-thread-1 處理了 task95 任務(wù)
這里是xhJaver,線程池系列 當(dāng)前線程名字是 pool-1-thread-1 處理了 task4 任務(wù)
這里是xhJaver芒率,線程池系列 當(dāng)前線程名字是 pool-1-thread-1 處理了 task3 任務(wù)
這里是xhJaver囤耳,線程池系列 當(dāng)前線程名字是 pool-1-thread-1 處理了 task2 任務(wù)
這里是xhJaver,線程池系列 當(dāng)前線程名字是 pool-1-thread-1 處理了 task1 任務(wù)
由輸出結(jié)果可見(jiàn),除了第一個(gè)以外充择,處理任務(wù)的順序會(huì)按照優(yōu)先級(jí)大小先處理
八德玫、幾種常見(jiàn)的線程池及注意點(diǎn)
他們分別是以下幾種
1.newFixedThreadPool
- Executors.newFixedThreadPool(10) 它的構(gòu)造方法是
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
有此可見(jiàn),這個(gè)FixedThreadPool線程池的核心線程數(shù)和最大線程數(shù)一樣椎麦,所以就沒(méi)有非核心線程數(shù)宰僧,存活時(shí)間這個(gè)參數(shù)也就是無(wú)效的了,它底層用的是LinkedBlockingQueue這個(gè)阻塞隊(duì)列观挎,這個(gè)隊(duì)列是個(gè)無(wú)界隊(duì)列琴儿,可以點(diǎn)進(jìn)去源碼看它默認(rèn)的容量是Integer.MAX_VALUE
public LinkedBlockingQueue() {
this(Integer.MAX_VALUE);
}
所以這會(huì)導(dǎo)致一個(gè)什么問(wèn)題呢?就會(huì)導(dǎo)致键兜,當(dāng)核心線程都跑滿的時(shí)候凤类,再來(lái)新任務(wù)的話就會(huì)不斷的添加至這個(gè)阻塞隊(duì)列里面穗泵,一直加一直加普气,但是內(nèi)存是有限的,所以有可能會(huì)出現(xiàn) OOM(OutOfMemory) 的問(wèn)題
- Executors.newFixedThreadPool(10,Executors.defaultThreadFactory()); 這個(gè)構(gòu)造方法可以傳過(guò)來(lái)指定的創(chuàng)建線程的工廠
public static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>(),
threadFactory);
}
2.newCachedThreadPool
- Executors.newCachedThreadPool() 它的構(gòu)造方法是
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
由此可見(jiàn)佃延,它的核心線程數(shù)默認(rèn)是0现诀,線程池總線程容量是Integer.MAX_VALUE,阻塞隊(duì)列用的是SynchronousQueue同步隊(duì)列履肃,非核心線程數(shù)的空閑存活時(shí)間為60s,這會(huì)導(dǎo)致一個(gè)什么問(wèn)題呢仔沿?只要來(lái)了一個(gè)任務(wù),如果沒(méi)有線程的話就創(chuàng)建一個(gè)非核心線程去跑這個(gè)任務(wù)尺棋,如果跑著的過(guò)程中又來(lái)了一個(gè)任務(wù)封锉,就會(huì)繼續(xù)創(chuàng)建線程去跑,以此類(lèi)推膘螟,內(nèi)存是有限的成福,不斷的創(chuàng)建線程的話也會(huì)觸發(fā)OOM問(wèn)題
- Executors.newCachedThreadPool(Executors.defaultThreadFactory()) 這個(gè)構(gòu)造方法可以傳過(guò)來(lái)指定的創(chuàng)建線程的工廠
public static ExecutorService newCachedThreadPool(ThreadFactory threadFactory) {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>(),
threadFactory);
}
3.newSingleThreadExecutor
- Executors.newSingleThreadExecutor() 它的構(gòu)造方法是
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
我們可以看出,它的核心線程數(shù)是一個(gè)荆残,總線程數(shù)也是一個(gè)奴艾。底層用的是LinkedBlockingQueue阻塞隊(duì)列 當(dāng)來(lái)任務(wù)的時(shí)候線程池如果沒(méi)有線程的話,則創(chuàng)建一個(gè)也是唯一一個(gè)線程來(lái)執(zhí)行任務(wù)内斯,剩下的任務(wù)都會(huì)被塞進(jìn)無(wú)界阻塞隊(duì)列里面蕴潦,也是會(huì)有可能產(chǎn)生OOM問(wèn)題。
- Executors.newSingleThreadExecutor(Executors.defaultThreadFactory()) 這個(gè)構(gòu)造方法可以傳過(guò)來(lái)指定的創(chuàng)建線程的工廠
public static ExecutorService newSingleThreadExecutor(ThreadFactory threadFactory) {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>(),
threadFactory));
}
九俘闯、拓展線程池
什么潭苞?線程池還可以拓展?真朗!是的萄传,如果我想記錄下每個(gè)任務(wù)的執(zhí)行開(kāi)始情況,結(jié)束情況,線程池關(guān)閉情況就要拓展啦秀菱,ThreadPoolExecutor它內(nèi)部是提供了幾個(gè)方法給我們拓展振诬,其中beforeExecute、afterExecute衍菱、terminated赶么,這三個(gè)分別對(duì)應(yīng)任務(wù)開(kāi)始,任務(wù)結(jié)束脊串,線程池關(guān)閉的三種情況辫呻,所以我們就要重寫(xiě)他們啦,話不多說(shuō)琼锋,看下代碼
public static void main(String[] args) {
//阻塞隊(duì)列放闺,設(shè)置阻塞任務(wù)最多為10個(gè)
BlockingQueue<Runnable> blockingQueue = new ArrayBlockingQueue<Runnable> (10);
//創(chuàng)建線程安全的計(jì)數(shù)器
AtomicInteger atomicInteger = new AtomicInteger();
//自定義線程工廠
ThreadFactory threadFactory = new DIYThreadFactory(atomicInteger);
//自定義拒絕策略 當(dāng)線程池的最大工作線程跑滿以及阻塞隊(duì)列滿了的話,會(huì)由拒絕策略處理剩下的任務(wù)
DIYRejectedHandler diyRejectedHandler = new DIYRejectedHandler();
//創(chuàng)建線程池 核心線程數(shù)為5 最大線程數(shù)為10 非核心線程空閑存活時(shí)間為60s
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(5, 10, 60L,
TimeUnit.SECONDS, blockingQueue, threadFactory, diyRejectedHandler
){
@Override
protected void beforeExecute(Thread t, Runnable r) {
System.out.println("xhJaver 當(dāng)前線程是"+t.getName()+"開(kāi)始處理任務(wù):"+r.toString());
}
@Override
protected void afterExecute(Runnable r, Throwable t) {
if(t!=null){
System.out.println("xhJaver 當(dāng)前線程是"+Thread.currentThread().getName() +"處理任務(wù)結(jié)束:"+r.toString()+" 錯(cuò)誤是 "+ t);
}
System.out.println("xhJaver 當(dāng)前線程是"+Thread.currentThread().getName() +"處理任務(wù)結(jié)束:"+r.toString()+" 沒(méi)有錯(cuò)誤 ");
}
@Override
protected void terminated() {
System.out.println("xhJaver 當(dāng)前線程是"+Thread.currentThread().getName() +"關(guān)閉線程池");
}
};
for (int i=0;i<21;i++){
//創(chuàng)建10個(gè)任務(wù)缕坎,如果要是創(chuàng)建>20個(gè)任務(wù)怖侦,則20以外的任務(wù)會(huì)交由拒絕策略處理
Task task = new Task("task" + i);
//讓我們自定義的線程池去跑這些任務(wù)
threadPoolExecutor.execute(task);
}
//記得要關(guān)閉線程池
threadPoolExecutor.shutdown();
}
輸出結(jié)果是
這是xhJaver無(wú)法處理的任務(wù) Task{taskName='task20'} 當(dāng)前線程名字是 main
xhJaver 當(dāng)前線程是xhJaver-thread-7開(kāi)始處理任務(wù):Task{taskName='task17'}
xhJaver 當(dāng)前線程是xhJaver-thread-6開(kāi)始處理任務(wù):Task{taskName='task16'}
xhJaver 當(dāng)前線程是xhJaver-thread-9開(kāi)始處理任務(wù):Task{taskName='task19'}
xhJaver 當(dāng)前線程是xhJaver-thread-4開(kāi)始處理任務(wù):Task{taskName='task4'}
xhJaver 當(dāng)前線程是xhJaver-thread-8開(kāi)始處理任務(wù):Task{taskName='task18'}
xhJaver 當(dāng)前線程是xhJaver-thread-2開(kāi)始處理任務(wù):Task{taskName='task2'}
xhJaver 當(dāng)前線程是xhJaver-thread-3開(kāi)始處理任務(wù):Task{taskName='task3'}
xhJaver 當(dāng)前線程是xhJaver-thread-5開(kāi)始處理任務(wù):Task{taskName='task15'}
xhJaver 當(dāng)前線程是xhJaver-thread-0開(kāi)始處理任務(wù):Task{taskName='task0'}
xhJaver 當(dāng)前線程是xhJaver-thread-1開(kāi)始處理任務(wù):Task{taskName='task1'}
這里是xhJaver,線程池系列 當(dāng)前線程名字是 xhJaver-thread-4 處理了 task4 任務(wù)
xhJaver 當(dāng)前線程是xhJaver-thread-4處理任務(wù)結(jié)束:Task{taskName='task4'} 沒(méi)有錯(cuò)誤
xhJaver 當(dāng)前線程是xhJaver-thread-4開(kāi)始處理任務(wù):Task{taskName='task5'}
這里是xhJaver谜叹,線程池系列 當(dāng)前線程名字是 xhJaver-thread-9 處理了 task19 任務(wù)
xhJaver 當(dāng)前線程是xhJaver-thread-9處理任務(wù)結(jié)束:Task{taskName='task19'} 沒(méi)有錯(cuò)誤
這里是xhJaver匾寝,線程池系列 當(dāng)前線程名字是 xhJaver-thread-6 處理了 task16 任務(wù)
xhJaver 當(dāng)前線程是xhJaver-thread-6處理任務(wù)結(jié)束:Task{taskName='task16'} 沒(méi)有錯(cuò)誤
xhJaver 當(dāng)前線程是xhJaver-thread-9開(kāi)始處理任務(wù):Task{taskName='task6'}
這里是xhJaver,線程池系列 當(dāng)前線程名字是 xhJaver-thread-7 處理了 task17 任務(wù)
xhJaver 當(dāng)前線程是xhJaver-thread-7處理任務(wù)結(jié)束:Task{taskName='task17'} 沒(méi)有錯(cuò)誤
xhJaver 當(dāng)前線程是xhJaver-thread-7開(kāi)始處理任務(wù):Task{taskName='task8'}
xhJaver 當(dāng)前線程是xhJaver-thread-6開(kāi)始處理任務(wù):Task{taskName='task7'}
這里是xhJaver荷腊,線程池系列 當(dāng)前線程名字是 xhJaver-thread-1 處理了 task1 任務(wù)
這里是xhJaver艳悔,線程池系列 當(dāng)前線程名字是 xhJaver-thread-8 處理了 task18 任務(wù)
xhJaver 當(dāng)前線程是xhJaver-thread-8處理任務(wù)結(jié)束:Task{taskName='task18'} 沒(méi)有錯(cuò)誤
xhJaver 當(dāng)前線程是xhJaver-thread-8開(kāi)始處理任務(wù):Task{taskName='task9'}
這里是xhJaver,線程池系列 當(dāng)前線程名字是 xhJaver-thread-2 處理了 task2 任務(wù)
xhJaver 當(dāng)前線程是xhJaver-thread-2處理任務(wù)結(jié)束:Task{taskName='task2'} 沒(méi)有錯(cuò)誤
xhJaver 當(dāng)前線程是xhJaver-thread-2開(kāi)始處理任務(wù):Task{taskName='task10'}
這里是xhJaver女仰,線程池系列 當(dāng)前線程名字是 xhJaver-thread-3 處理了 task3 任務(wù)
xhJaver 當(dāng)前線程是xhJaver-thread-3處理任務(wù)結(jié)束:Task{taskName='task3'} 沒(méi)有錯(cuò)誤
這里是xhJaver猜年,線程池系列 當(dāng)前線程名字是 xhJaver-thread-5 處理了 task15 任務(wù)
xhJaver 當(dāng)前線程是xhJaver-thread-5處理任務(wù)結(jié)束:Task{taskName='task15'} 沒(méi)有錯(cuò)誤
xhJaver 當(dāng)前線程是xhJaver-thread-5開(kāi)始處理任務(wù):Task{taskName='task12'}
xhJaver 當(dāng)前線程是xhJaver-thread-1處理任務(wù)結(jié)束:Task{taskName='task1'} 沒(méi)有錯(cuò)誤
xhJaver 當(dāng)前線程是xhJaver-thread-1開(kāi)始處理任務(wù):Task{taskName='task13'}
這里是xhJaver,線程池系列 當(dāng)前線程名字是 xhJaver-thread-0 處理了 task0 任務(wù)
xhJaver 當(dāng)前線程是xhJaver-thread-3開(kāi)始處理任務(wù):Task{taskName='task11'}
xhJaver 當(dāng)前線程是xhJaver-thread-0處理任務(wù)結(jié)束:Task{taskName='task0'} 沒(méi)有錯(cuò)誤
xhJaver 當(dāng)前線程是xhJaver-thread-0開(kāi)始處理任務(wù):Task{taskName='task14'}
這里是xhJaver疾忍,線程池系列 當(dāng)前線程名字是 xhJaver-thread-4 處理了 task5 任務(wù)
xhJaver 當(dāng)前線程是xhJaver-thread-4處理任務(wù)結(jié)束:Task{taskName='task5'} 沒(méi)有錯(cuò)誤
這里是xhJaver乔外,線程池系列 當(dāng)前線程名字是 xhJaver-thread-6 處理了 task7 任務(wù)
xhJaver 當(dāng)前線程是xhJaver-thread-6處理任務(wù)結(jié)束:Task{taskName='task7'} 沒(méi)有錯(cuò)誤
這里是xhJaver,線程池系列 當(dāng)前線程名字是 xhJaver-thread-9 處理了 task6 任務(wù)
xhJaver 當(dāng)前線程是xhJaver-thread-9處理任務(wù)結(jié)束:Task{taskName='task6'} 沒(méi)有錯(cuò)誤
這里是xhJaver锭碳,線程池系列 當(dāng)前線程名字是 xhJaver-thread-7 處理了 task8 任務(wù)
xhJaver 當(dāng)前線程是xhJaver-thread-7處理任務(wù)結(jié)束:Task{taskName='task8'} 沒(méi)有錯(cuò)誤
這里是xhJaver袁稽,線程池系列 當(dāng)前線程名字是 xhJaver-thread-2 處理了 task10 任務(wù)
xhJaver 當(dāng)前線程是xhJaver-thread-2處理任務(wù)結(jié)束:Task{taskName='task10'} 沒(méi)有錯(cuò)誤
這里是xhJaver,線程池系列 當(dāng)前線程名字是 xhJaver-thread-8 處理了 task9 任務(wù)
xhJaver 當(dāng)前線程是xhJaver-thread-8處理任務(wù)結(jié)束:Task{taskName='task9'} 沒(méi)有錯(cuò)誤
這里是xhJaver擒抛,線程池系列 當(dāng)前線程名字是 xhJaver-thread-5 處理了 task12 任務(wù)
xhJaver 當(dāng)前線程是xhJaver-thread-5處理任務(wù)結(jié)束:Task{taskName='task12'} 沒(méi)有錯(cuò)誤
這里是xhJaver推汽,線程池系列 當(dāng)前線程名字是 xhJaver-thread-0 處理了 task14 任務(wù)
xhJaver 當(dāng)前線程是xhJaver-thread-0處理任務(wù)結(jié)束:Task{taskName='task14'} 沒(méi)有錯(cuò)誤
這里是xhJaver,線程池系列 當(dāng)前線程名字是 xhJaver-thread-3 處理了 task11 任務(wù)
xhJaver 當(dāng)前線程是xhJaver-thread-3處理任務(wù)結(jié)束:Task{taskName='task11'} 沒(méi)有錯(cuò)誤
這里是xhJaver歧沪,線程池系列 當(dāng)前線程名字是 xhJaver-thread-1 處理了 task13 任務(wù)
xhJaver 當(dāng)前線程是xhJaver-thread-1處理任務(wù)結(jié)束:Task{taskName='task13'} 沒(méi)有錯(cuò)誤
xhJaver 當(dāng)前線程是xhJaver-thread-1關(guān)閉線程池
更多精彩請(qǐng)關(guān)注公眾號(hào)xhJaver,京東java工程師和你一起成長(zhǎng)