非常適合新手學(xué)生的Java線程池超詳細(xì)分析
線程池的好處
- 可以實(shí)現(xiàn)線程的復(fù)用,避免重新創(chuàng)建線程和銷毀線程。創(chuàng)建線程和銷毀線程對CPU的開銷是很大的。
- 可以限制最大可創(chuàng)建的線程數(shù),可根據(jù)自己的機(jī)器性能動態(tài)調(diào)整線程池參數(shù),提高應(yīng)用性能。
- 提供定時執(zhí)行、并發(fā)數(shù)控制等功能。
- 統(tǒng)一管理線程。
創(chuàng)建線程池的五種方式
1:緩存線程池(不推薦)
2:固定容量線程池(不推薦)
3:單個線程池(不推薦)
4:定時任務(wù)線程池(不推薦)
5:通過ThreadPoolExecutor構(gòu)造方法創(chuàng)建線程池(阿里巴巴開發(fā)手冊十分推薦)
前面4種創(chuàng)建線程池的方式都是通過Executors的靜態(tài)方法來創(chuàng)建。
緩存線程池CachedThreadPool
ExecutorService executorService = Executors.newCachedThreadPool();
for (int i = 0; i < 10; i++) {
final int finalI = i;
executorService.execute(new Runnable() {
public void run() {
System.out.println(Thread.currentThread().getName()+"<thread->run>"+ finalI);
}
});
}為什么不推薦使用緩存線程池?
源碼分析
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, 2147483647, 60L, TimeUnit.SECONDS, new SynchronousQueue());
}
public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, Executors.defaultThreadFactory(), defaultHandler);
}通過上面兩個代碼片段,我們可以看出CachedThreadPool的maximumPoolSize為Integer的最大值2147483647,相當(dāng)于可以無限的創(chuàng)建線程,而創(chuàng)建線程是需要內(nèi)存的,這樣就會造成內(nèi)存溢出,而且一般的機(jī)器也沒用那么大的內(nèi)存給它創(chuàng)建這么大量的線程。
固定容量線程池FixedThreadPool
newFixedThreadPool(int num),num就是我們要指定的固定線程數(shù)量
ExecutorService executorService = Executors.newFixedThreadPool(5);
for (int i = 0; i < 10; i++) {
final int finalI = i;
executorService.execute(new Runnable() {
public void run() {
System.out.println(Thread.currentThread().getName()+"<thread->run>"+ finalI);
}
});
}輸出:
pool-1-thread-5<thread->run>4
pool-1-thread-4<thread->run>3
pool-1-thread-5<thread->run>5
pool-1-thread-3<thread->run>2
pool-1-thread-3<thread->run>8
pool-1-thread-3<thread->run>9
pool-1-thread-2<thread->run>1
pool-1-thread-1<thread->run>0
pool-1-thread-5<thread->run>7
pool-1-thread-4<thread->run>6
可以看出起到了線程的復(fù)用。
為什么FixedThreadPool是固定線程池?
源碼分析
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue());
}通過這個源碼可以看出,核心線程數(shù)(corePoolSize)和最大線程數(shù)(maximumPoolSize)都為nThreads,因?yàn)橹挥羞@樣,線程池才不會進(jìn)行擴(kuò)容,線程數(shù)才固定。
單個線程池SingleThreadExecutor
ExecutorService executorService = Executors.newSingleThreadExecutor();
for (int i = 0; i < 10; i++) {
final int finalI = i;
executorService.execute(new Runnable() {
public void run() {
System.out.println(Thread.currentThread().getName()+"<thread->run>"+ finalI);
}
});
}為什么SingleThreadExecutor只含有一個線程?
源碼分析
public static ExecutorService newSingleThreadExecutor() {
return new Executors.FinalizableDelegatedExecutorService(new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue()));
}通過這個源碼可以看出,核心線程數(shù)(corePoolSize)和最大線程數(shù)(maximumPoolSize)都為1,所以它只含有一個線程。
定時任務(wù)線程池ScheduledThreadPool
int initDelay=10; //初始化延時
int period=1;//初始化延遲過了之后,每秒的延時
ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(10);
scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"<thread->run>");
}
},initDelay,period, TimeUnit.SECONDS);這段代碼的效果是:程序運(yùn)行之后等10秒,然后輸出第一次結(jié)果,之后每隔1秒輸出一次結(jié)果。
為什么不推薦使用ScheduledThreadPool?
源碼分析
public ScheduledThreadPoolExecutor(int corePoolSize) {
super(corePoolSize, 2147483647, 10L, TimeUnit.MILLISECONDS, new ScheduledThreadPoolExecutor.DelayedWorkQueue());
}可以看出ScheduledThreadPool的最大線程數(shù)(maximumPoolSize)為Integer的最大值2147483647,相當(dāng)于可以無限的創(chuàng)建線程,而創(chuàng)建線程是需要內(nèi)存的,這樣就會造成內(nèi)存溢出,而且一般的機(jī)器也沒用那么大的內(nèi)存給它創(chuàng)建這么大量的線程。
ThreadPoolExecutor創(chuàng)建線程池(十分推薦)
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(10, 20,
2L, TimeUnit.SECONDS, new ArrayBlockingQueue<>(5),
Executors.defaultThreadFactory(), new ThreadPoolExecutor.AbortPolicy());
for (int i = 0; i < 12; i++) {
final int finalI = i;
threadPoolExecutor.execute(new Runnable() {
public void run() {
System.out.println(Thread.currentThread().getName()+"<thread->run>"+ finalI);
}
});
}ThreadPoolExecutor的七個參數(shù)詳解
public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler) {
}- corePoolSize:核心線程數(shù)。這些線程一旦被創(chuàng)建不會被銷毀,是一直存在的。線程池默認(rèn)是沒有線程的,當(dāng)有任務(wù)到來了,就會通過ThreadFactory去創(chuàng)建線程,并一直存在。
- maximumPoolSize:最大線程數(shù)。非核心線程數(shù)=maximumPoolSize-corePoolSize,非核心線程數(shù)其實(shí)就是可擴(kuò)容的線程數(shù),可能會被銷毀。
- keepAliveTime:非核心線程的空閑存活時間。當(dāng)通過擴(kuò)容生成的非核心線程數(shù)在keepAliveTime這個時間后還處于空閑狀態(tài),則會銷毀這些非核心線程。
- unit:keepAliveTime的時間單位,例如:秒
- workQueue:等待區(qū)。當(dāng)來了>corePoolSize的任務(wù)時會把任務(wù)存放在workQueue這個阻塞隊列中,等待其他線程處理。
- threadFactory:線程工廠。創(chuàng)建線程的一種方式。
- handler:拒絕策略。當(dāng)來了>最大線程數(shù)+workQueue的容量則會執(zhí)行拒絕策略
workQueue
ArrayBlockingQueue:有界阻塞隊列。隊列有大小限制,當(dāng)容量超過時則會觸發(fā)擴(kuò)容或者拒絕策略。
public ArrayBlockingQueue(int capacity) {
this(capacity, false);
}LinkedBlockingQueue:無界阻塞隊列,隊列無大小限制,可能會造成內(nèi)存溢出。
public LinkedBlockingQueue() {
this(2147483647);
}handler
AbortPolicy:直接拋異常
public static class AbortPolicy implements RejectedExecutionHandler {
public AbortPolicy() {
}
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
throw new RejectedExecutionException("Task " + r.toString() + " rejected from " + e.toString());
}
}DiscardPolicy:不作任何操作。默默丟棄任務(wù)
public static class DiscardPolicy implements RejectedExecutionHandler {
public DiscardPolicy() {
}
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
}
}DiscardOldestPolicy:丟掉存在時間最長的任務(wù)
public static class DiscardOldestPolicy implements RejectedExecutionHandler {
public DiscardOldestPolicy() {
}
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
if (!e.isShutdown()) {
e.getQueue().poll();
e.execute(r);
}
}
}CallerRunsPolicy:讓提交任務(wù)的線程去處理任務(wù)
public static class CallerRunsPolicy implements RejectedExecutionHandler {
public CallerRunsPolicy() {
}
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
if (!e.isShutdown()) {
r.run();
}
}
}threadFactory
ThreadFactory threadFactory = Executors.defaultThreadFactory();
threadFactory.newThread(new Runnable() {
@Override
public void run() {
System.out.println("threadFactory");
}
}).start();如何觸發(fā)拒絕策略和線程池擴(kuò)容?
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(10, 20,
2L, TimeUnit.SECONDS, new ArrayBlockingQueue<>(5),
Executors.defaultThreadFactory(), new ThreadPoolExecutor.AbortPolicy());
for (int i = 0; i < 26; i++) { //并發(fā)數(shù)26
final int finalI = i;
threadPoolExecutor.execute(new Runnable() {
public void run() {
System.out.println(Thread.currentThread().getName()+"<thread->run>"+ finalI);
}
});
}
/**
* 核心線程數(shù)=10,最大線程數(shù)=20,故可擴(kuò)容線程數(shù)=20-10
* BlockingQueue的大小為5,故等待區(qū)的大小為5,也就是當(dāng)并發(fā)數(shù)<=核心線程數(shù)+5不會擴(kuò)容,并發(fā)數(shù)大于16才會擴(kuò)容
*
* 觸發(fā)擴(kuò)容:并發(fā)數(shù)>核心線程數(shù)+阻塞隊列的大小
* 對于這段代碼,如果來了26個并發(fā),10個并發(fā)會被核心線程處理,5個會在等待區(qū),剩下11個會因?yàn)榈却齾^(qū)滿了而觸發(fā)擴(kuò)容
* 因?yàn)檫@里最多能夠擴(kuò)容10個,這里卻是11個,所以會觸發(fā)拒絕策略
*/- 為什么這段代碼會觸發(fā)拒絕策略
對于這段代碼,如果來了26個并發(fā),10個并發(fā)會被核心線程處理,5個會在等待區(qū),剩下11個會因?yàn)榈却齾^(qū)滿了而觸發(fā)擴(kuò)容,但是又因?yàn)橐驗(yàn)檫@里最多能夠擴(kuò)容10個,這里卻是11個,所以會觸發(fā)拒絕策略。
- 怎么觸發(fā)擴(kuò)容
觸發(fā)擴(kuò)容:并發(fā)數(shù)>核心線程數(shù)(corePoolSize)+阻塞隊列(workQueue)的大小
- 使用Java純手寫一個線程池
下期文章鏈接http://www.dhdzp.com/article/241589.htm
到此這篇關(guān)于非常適合新手學(xué)生的Java線程池超詳細(xì)分析的文章就介紹到這了,更多相關(guān)Java 線程池內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
springboot的SpringPropertyAction事務(wù)屬性源碼解讀
這篇文章主要介紹了springboot的SpringPropertyAction事務(wù)屬性源碼解讀,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-11-11
Java 實(shí)戰(zhàn)項(xiàng)目錘煉之IT設(shè)備固定資產(chǎn)管理系統(tǒng)的實(shí)現(xiàn)流程
讀萬卷書不如行萬里路,只學(xué)書上的理論是遠(yuǎn)遠(yuǎn)不夠的,只有在實(shí)戰(zhàn)中才能獲得能力的提升,本篇文章手把手帶你用Java+SSM+jsp+mysql+maven實(shí)現(xiàn)一個IT設(shè)備固定資產(chǎn)管理系統(tǒng),大家可以在過程中查缺補(bǔ)漏,提升水平2021-11-11
Netty分布式NioSocketChannel注冊到selector方法解析
這篇文章主要為大家介紹了Netty分布式源碼分析NioSocketChannel注冊到selector方法的解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-03-03
eclipse連接數(shù)據(jù)庫并實(shí)現(xiàn)用戶注冊登錄功能
這篇文章主要介紹了eclipse連接數(shù)據(jù)庫并實(shí)現(xiàn)用戶注冊登錄功能的相關(guān)資料,需要的朋友可以參考下2021-01-01

