Java線程池八股及實(shí)現(xiàn)過程
線程池的八股
1、說一下線程池的核心參數(shù)?
ThreadPoolExecutor線程池的核心參數(shù)有7個(gè),1.核心線程數(shù)目(corePoolSize)、2.最大線程數(shù)目(maximumPoolSize),最大線程數(shù)目=核心線程+臨時(shí)線程、3.生存時(shí)間(keepAliveTime),是臨時(shí)線程的生存時(shí)間,生存時(shí)間內(nèi)沒有新任務(wù),臨時(shí)線程會(huì)釋放、4.時(shí)間單位(unit)、5.阻塞隊(duì)列(workQueue),當(dāng)核心線程滿了之后新來的任務(wù)會(huì)進(jìn)入隊(duì)列排隊(duì),隊(duì)列滿后會(huì)創(chuàng)建臨時(shí)線程執(zhí)行任務(wù)、6.線程工廠(threadFactory),定制線程對(duì)象的創(chuàng)建,例如設(shè)置線程名字,是否是守護(hù)線程等、7.拒絕策略(handler),當(dāng)所有線程都在忙,workQueue也放滿時(shí),就會(huì)觸發(fā)拒絕策略。
2、線程池的執(zhí)行原理?
首先提交任務(wù)會(huì)判斷核心線程是否已滿,如果沒滿就正常工作,如果已滿的話就會(huì)判斷這個(gè)阻塞隊(duì)列是否已滿,如果阻塞隊(duì)列沒滿就放入阻塞隊(duì)列等待,如果阻塞隊(duì)列也滿了,就判斷線程數(shù)是否小于最大線程數(shù),如果小于就創(chuàng)建臨時(shí)線程,這時(shí)臨時(shí)線程默認(rèn)是來執(zhí)行新來的任務(wù),而不是執(zhí)行從阻塞隊(duì)列中取出的任務(wù)來執(zhí)行,如果不小于,那就走拒絕策略。
3、拒絕策略有哪些?
拒絕策略有4種,1.AbortPolicy,直接拋異常,默認(rèn)策略。2.CallerRunsPolicy,使用調(diào)用者所在線程來執(zhí)行任務(wù),如主線程main。3.DiscardOldestPolicy,丟棄阻塞隊(duì)列最靠前的任務(wù),來執(zhí)行當(dāng)前任務(wù)。4.DiscardPolicy,直接丟棄任務(wù)。
4、線程池中有哪些常見的阻塞隊(duì)列?
有4種常見的阻塞隊(duì)列,1.ArrayBlockingQueue,基于數(shù)組的有界阻塞隊(duì)列,F(xiàn)IFO。2.LinkedBlockingQueue,基于鏈表的有界阻塞隊(duì)列,F(xiàn)IFO。3.DelayedWorkQueue,優(yōu)先級(jí)阻塞隊(duì)列,保證每次出任務(wù)的是執(zhí)行時(shí)間最靠前的任務(wù)。4.SynchronousQueue,不存儲(chǔ)元素的阻塞隊(duì)列,每次插入操作都要等待一個(gè)移除操作。
5、ArrayBlockingQueue和LinkedBlockingQueue有什么區(qū)別?
LinkedBlockingQueue默認(rèn)是無界的,支持有界,ArrayBlockingQueue強(qiáng)制有界。LinkedBlockingQueue底層是鏈表,ArrayBlockingQueue底層是數(shù)組。LinkedBlockingQueue是懶惰的,創(chuàng)建節(jié)點(diǎn)時(shí)添加數(shù)據(jù),入隊(duì)會(huì)生成新Node,ArrayBlockingQueue是提前初始化Node數(shù)組,Node需要是提前創(chuàng)建好的。LinkedBlockingQueue是兩把鎖,鏈表的頭和尾加鎖,ArrayBlockingQueue是全部一把鎖,一般平時(shí)常用的就是LinkedBlockingQueue。
6、如何確定核心線程數(shù)?
分IO密集型任務(wù)和CPU密集型任務(wù),注:N為CPU核數(shù)。如果并發(fā)不高、執(zhí)行時(shí)間長(zhǎng)的話,設(shè)置為IO密集型任務(wù)核心線程數(shù)就是2N+1;設(shè)置為CPU密集型任務(wù)核心線程數(shù)就是N+1。如果高并發(fā)、任務(wù)執(zhí)行時(shí)間短的話核心線程數(shù)就是N+1,減少線程上下文切換。
7、線程池的種類有哪些?
常見的有4種,1.創(chuàng)建使用固定線程數(shù)的線程池,就是核心線程數(shù)和最大線程數(shù)一樣,沒有臨時(shí)線程,阻塞隊(duì)列是LinkedBlockingQueue。2.單線程化的線程池,核心線程數(shù)和最大線程數(shù)都是1,阻塞隊(duì)列是LinkedBlockingQueue。3.可緩存線程池,核心線程數(shù)為0,只有臨時(shí)線程數(shù),阻塞隊(duì)列是SynchronousQueue,不存儲(chǔ)元素的阻塞隊(duì)列,每次插入操作都要等待一個(gè)移除操作。3.提供了“延遲”和“周期執(zhí)行”功能的ThreadPoolExecutor,可以執(zhí)行延遲任務(wù)的線程池,支持定時(shí)及周期性完成任務(wù)。
8、為什么不建議使用Executors創(chuàng)建線程池?
可能會(huì)堆積大量的請(qǐng)求,創(chuàng)建大量的對(duì)象,導(dǎo)致OOM(內(nèi)存溢出)。
9、談?wù)勀銓?duì)ThreadLocal的理解?
為每個(gè)線程都分配一個(gè)獨(dú)立的線程副本,讓多個(gè)線程只操作自己內(nèi)部的值,解決了并發(fā)訪問沖突問題,同時(shí)實(shí)現(xiàn)了線程內(nèi)的資源共享。
10、你知道ThreadLocal內(nèi)存泄露問題嗎?
線程內(nèi)都維護(hù)了ThreadLocalMap成員變量,用來存儲(chǔ)資源對(duì)象,調(diào)用set()方法,以ThreadLocal作為key,資源對(duì)象作為value,放入ThreadLocalMap集合中,然后調(diào)用get()方法到線程中查找自己key關(guān)聯(lián)的資源。然后呢,ThreadLocal有用到弱引用和強(qiáng)引用,弱引用的內(nèi)存會(huì)被回收,而強(qiáng)引用不會(huì)被回收,積累滿了就可能發(fā)生內(nèi)存泄露問題,可以使用remove()移除當(dāng)前線程資源來解決內(nèi)存泄露問題。
線程池的實(shí)現(xiàn)
配置類:
@Configuration
public class ThreadPoolExecutorConfig {
@Bean
public ThreadPoolExecutor threadPoolExecutor() {
// 創(chuàng)建線程工廠
ThreadFactory threadFactory = new ThreadFactory() {
// 初始化線程數(shù)為1
private int count =1;
@Override
public Thread newThread(@NotNull Runnable r) {
Thread thread = new Thread(r);
thread.setName("線程" + count);
count++;
return thread;
}
};
return new ThreadPoolExecutor(2, 3, 1000, TimeUnit.SECONDS, new ArrayBlockingQueue<>(4), threadFactory);
}
}演示線程池工作流程(核心線程數(shù):2;最大線程池:3;阻塞隊(duì)列可存放任務(wù):4):
/**
* 線程池隊(duì)列測(cè)試接口
*/
@RestController
@RequestMapping("/queue")
@Slf4j
public class QueueController {
@Resource
private ThreadPoolExecutor threadPoolExecutor;
// 核心線程數(shù):2;最大線程池:3;阻塞隊(duì)列可存放任務(wù):4
@GetMapping("/add")
public void add(String taskName){
// 創(chuàng)建任務(wù)
CompletableFuture.runAsync(()->{
log.info("當(dāng)前線程:" + Thread.currentThread().getName() + "," + "正在執(zhí)行任務(wù)" + taskName);
try {
Thread.sleep(1000000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
},threadPoolExecutor);
}
@GetMapping("/get")
public String get(){
Map<String, Object> map = new HashMap<>();
long taskCount = threadPoolExecutor.getTaskCount();
map.put("任務(wù)總數(shù)", taskCount);
long completedTaskCount = threadPoolExecutor.getCompletedTaskCount();
map.put("已完成任務(wù)數(shù)", completedTaskCount);
int activeCount = threadPoolExecutor.getActiveCount();
map.put("正在執(zhí)行任務(wù)的線程數(shù)", activeCount);
int size = threadPoolExecutor.getQueue().size();
map.put("隊(duì)列長(zhǎng)度", size);
return JSONUtil.toJsonStr(map);
}
}首先獲取到當(dāng)前狀態(tài)情況:




以此類推發(fā)送任務(wù)2后執(zhí)行情況如下:


兩個(gè)核心線程占滿,按預(yù)期接著發(fā)送任務(wù)應(yīng)該放到隊(duì)列中,隊(duì)列長(zhǎng)度+1:

因?yàn)槲覀冏枞?duì)列可以存放4個(gè)任務(wù),以此類推發(fā)滿4個(gè)隊(duì)列長(zhǎng)度后再看看效果:

發(fā)送到任務(wù)7,也就是阻塞隊(duì)列占滿4個(gè)后再發(fā)送一個(gè)任務(wù):


任務(wù)總數(shù)為7,隊(duì)列長(zhǎng)度依舊為4,可以發(fā)現(xiàn)臨時(shí)線程默認(rèn)是來執(zhí)行新來的任務(wù),而不是執(zhí)行從阻塞隊(duì)列中取出的任務(wù)來執(zhí)行。
接著再發(fā)送一個(gè)任務(wù),由于當(dāng)前線程數(shù)=最大線程數(shù),并且隊(duì)列也滿,預(yù)期應(yīng)該是走默認(rèn)拒絕策略:


符合預(yù)期。
到此這篇關(guān)于Java線程池八股及實(shí)現(xiàn)的文章就介紹到這了,更多相關(guān)Java線程池八股內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
IDEA創(chuàng)建Gradle項(xiàng)目然后刪除報(bào)錯(cuò)問題及解決
這篇文章主要介紹了IDEA創(chuàng)建Gradle項(xiàng)目然后刪除報(bào)錯(cuò)問題及解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2025-04-04
Java源碼解析阻塞隊(duì)列ArrayBlockingQueue介紹
今天小編就為大家分享一篇關(guān)于Java源碼解析阻塞隊(duì)列ArrayBlockingQueue介紹,小編覺得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來看看吧2019-01-01
一文詳解Java如何創(chuàng)建和銷毀對(duì)象
Java由Sun Microsystems發(fā)明并在1995年發(fā)布,是世界上使用最廣泛的編程語言之一。本文主要和大家介紹一下Java是如何創(chuàng)建和銷毀對(duì)象的,希望對(duì)大家有所幫助2022-11-11
通過@Resource注解實(shí)現(xiàn)屬性裝配代碼詳解
這篇文章主要介紹了通過@Resource注解實(shí)現(xiàn)屬性裝配代碼詳解,具有一定借鑒價(jià)值,需要的朋友可以參考下2018-01-01
使用SpringAop動(dòng)態(tài)獲取mapper執(zhí)行的SQL,并保存SQL到Log表中
這篇文章主要介紹了使用SpringAop動(dòng)態(tài)獲取mapper執(zhí)行的SQL,并保存SQL到Log表中問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-03-03
一文吃透Spring?Cloud?gateway自定義錯(cuò)誤處理Handler
這篇文章主要為大家介紹了一文吃透Spring?Cloud?gateway自定義錯(cuò)誤處理Handler方法,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-03-03
java?ResourceBundle讀取properties文件方式
這篇文章主要介紹了java?ResourceBundle讀取properties文件方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-08-08

