ThreadPoolExecutor線(xiàn)程池參數(shù)

ThreadPoolExecutor線(xiàn)程池參數(shù)

1. 背景
公司一個(gè)業(yè)務(wù)需要循環(huán)多次(超過(guò)2000次),后將拿到的數(shù)據(jù)寫(xiě)入數(shù)據(jù)庫(kù)中。
2. 解決方法
基于業(yè)務(wù)的特點(diǎn),想到了兩個(gè)方法
1. 對(duì)數(shù)據(jù)庫(kù)大批量的寫(xiě)進(jìn)行分批次提交,基于JdbcTemplates,或者Batch提交
2. 創(chuàng)建多個(gè)線(xiàn)程池,使用CountDownLatch計(jì)數(shù)。
3. 應(yīng)用場(chǎng)景
應(yīng)用場(chǎng)景:線(xiàn)程的創(chuàng)建和銷(xiāo)毀是一個(gè)耗時(shí)的操作。如果在程序中頻繁的創(chuàng)建和銷(xiāo)毀線(xiàn)程,會(huì)對(duì)程序的反應(yīng)速度造成嚴(yán)重的影響。有時(shí)候可能導(dǎo)致crash掉。如果在頻繁的使用線(xiàn)程就可以使用線(xiàn)程池代替。
5.參數(shù)說(shuō)明
  1. corePoolSize:線(xiàn)程池基本大小 核心線(xiàn)程數(shù)

    1.  核心線(xiàn)程 數(shù)一定會(huì)存活,即使沒(méi)有任務(wù)需要執(zhí)行
    
    2. tasks(任務(wù)數(shù)) <coorePooSize 當(dāng)提交一個(gè)任務(wù)到線(xiàn)程池時(shí),線(xiàn)程池會(huì)創(chuàng)建一個(gè)線(xiàn)程來(lái)執(zhí)行任務(wù),即使其他空閑的基本線(xiàn)程能夠執(zhí)行新任務(wù)也會(huì)創(chuàng)建線(xiàn)程,tasks(任務(wù)數(shù)) > coorePooSize,會(huì)放入之后的隊(duì)列
    
    3. 設(shè)置allowCoreThreadTimeout=true(默認(rèn)false)時(shí),核心線(xiàn)程會(huì)超時(shí)關(guān)閉
    
    4. 如果調(diào)用了線(xiàn)程池的prestartAllCoreThreads方法,線(xiàn)程池會(huì)提前創(chuàng)建并啟動(dòng)所有基本線(xiàn)程。
    
  2. queueCapacity(任務(wù)隊(duì)列容量)

    1. ArrayBlockingQueue:是一個(gè)基于數(shù)組結(jié)構(gòu)的有界阻塞隊(duì)列,此隊(duì)列按 FIFO(先進(jìn)先出)原則對(duì)元素進(jìn)行排序
    
    2. LinkedBlockingQueue:一個(gè)基于鏈表結(jié)構(gòu)的阻塞隊(duì)列,此隊(duì)列按FIFO (先進(jìn)先出) 排序元素,吞吐量通常要高于ArrayBlockingQueue。靜態(tài)工廠方法Executors.newFixedThreadPool()使用了這個(gè)隊(duì)列
    
    3. SynchronousQueue:一個(gè)不存儲(chǔ)元素的阻塞隊(duì)列。每個(gè)插入操作必須等到另一個(gè)線(xiàn)程調(diào)用移除操作,否則插入操作一直處于阻塞狀態(tài),吞吐量通常要高于LinkedBlockingQueue,靜態(tài)工廠方法Executors.newCachedThreadPool使用了這個(gè)隊(duì)列。
    
    4. PriorityBlockingQueue:一個(gè)具有優(yōu)先級(jí)得無(wú)限阻塞隊(duì)列
    
  3. maxPoolSize 最大線(xiàn)程數(shù)

    1. 當(dāng)線(xiàn)程數(shù)>=corePoolSize,且任務(wù)隊(duì)列已滿(mǎn)時(shí)。線(xiàn)程池會(huì)創(chuàng)建新線(xiàn)程來(lái)處理任務(wù)
    2. 當(dāng)線(xiàn)程數(shù)=maxPoolSize,且任務(wù)隊(duì)列已滿(mǎn)時(shí),線(xiàn)程池會(huì)拒絕處理任務(wù)而拋出異常
    3. 如果使用了無(wú)界的任務(wù)隊(duì)列這個(gè)參數(shù)就沒(méi)什么效果
    
  4. keepAliveTime:線(xiàn)程空閑時(shí)間

    1. 當(dāng)線(xiàn)程空閑時(shí)間達(dá)到keepAliveTime時(shí),線(xiàn)程會(huì)退出,直到線(xiàn)程數(shù)量=corePoolSize
    2. 如果allowCoreThreadTimeout=true,則會(huì)直到線(xiàn)程數(shù)量=0
    
  5. RejectedExecutionHandler: 拒絕策略

    當(dāng)隊(duì)列和線(xiàn)程池都滿(mǎn)了,說(shuō)明線(xiàn)程池處于飽和狀態(tài),那么必須采取一種策略處理提交的新任務(wù)。這個(gè)策略默認(rèn)情況下是AbortPolicy,表示無(wú)法處理新任務(wù)時(shí)拋出異常。以下是JDK1.5提供的四種策略。

    1. ThreadPoolExecutor.AbortPolicy:
       當(dāng)線(xiàn)程池中的數(shù)量等于最大線(xiàn)程數(shù)時(shí)拋 java.util.concurrent.RejectedExecutionException 異                常,涉及到該異常的任務(wù)也不會(huì)被執(zhí)行,線(xiàn)程池默認(rèn)的拒絕策略就是該策略。
    2. ThreadPoolExecutor.DiscardPolicy():
       當(dāng)線(xiàn)程池中的數(shù)量等于最大線(xiàn)程數(shù)時(shí),默默丟棄不能執(zhí)行的新加任務(wù),不報(bào)任何異常。
    3. ThreadPoolExecutor.CallerRunsPolicy():
       當(dāng)線(xiàn)程池中的數(shù)量等于最大線(xiàn)程數(shù)時(shí),重試添加當(dāng)前的任務(wù);它會(huì)自動(dòng)重復(fù)調(diào)用execute()方法。
    4. ThreadPoolExecutor.DiscardOldestPolicy():
       當(dāng)線(xiàn)程池中的數(shù)量等于最大線(xiàn)程數(shù)時(shí),拋棄線(xiàn)程池中工作隊(duì)列頭部的任務(wù)(即等待時(shí)間最久的任務(wù)),并執(zhí)行當(dāng)前任務(wù)。
    
5. 線(xiàn)程池工作順序

1.當(dāng)線(xiàn)程池中線(xiàn)程數(shù)量小于 corePoolSize 則創(chuàng)建線(xiàn)程,并處理請(qǐng)求。

2. 當(dāng)線(xiàn)程池中線(xiàn)程數(shù)量大于等于 corePoolSize 時(shí),則把請(qǐng)求放入 workQueue 中,隨著線(xiàn)程池中的核心線(xiàn)程們不斷執(zhí)行任務(wù),只要線(xiàn)程池中有空閑的核心線(xiàn)程,線(xiàn)程池就從 workQueue 中取任務(wù)并處理。

3. 當(dāng) taskQueue 已存滿(mǎn),放不下新任務(wù)時(shí)則新建非核心線(xiàn)程入池,并處理請(qǐng)求直到線(xiàn)程數(shù)目達(dá)到 maximumPoolSize(最大線(xiàn)程數(shù)量設(shè)置值)。

4. 如果線(xiàn)程池中線(xiàn)程數(shù)大于 maximumPoolSize 則使用 RejectedExecutionHandler 來(lái)進(jìn)行任務(wù)拒絕處理。

6. 如何設(shè)置參數(shù)

參考了這篇:ThreadPoolExecutor線(xiàn)程池參數(shù)設(shè)置技巧

  • 默認(rèn)值
    • corePoolSize=1
    • queueCapacity=Integer.MAX_VALUE
    • maxPoolSize=Integer.MAX_VALUE
    • keepAliveTime=60s
    • allowCoreThreadTimeout=false
    • rejectedExecutionHandler=AbortPolicy()
  • 如何來(lái)設(shè)置
    • 需要根據(jù)幾個(gè)值來(lái)決定
      • tasks :每秒的任務(wù)數(shù),假設(shè)為500~1000
      • taskcost:每個(gè)任務(wù)花費(fèi)時(shí)間,假設(shè)為0.1s
      • responsetime:系統(tǒng)允許容忍的最大響應(yīng)時(shí)間,假設(shè)為1s
    • 做幾個(gè)計(jì)算
      • corePoolSize = 每秒需要多少個(gè)線(xiàn)程處理?
        • threadcount = tasks/(1/taskcost) =taskstaskcout = (500~1000)0.1 = 50~100 個(gè)線(xiàn)程。corePoolSize設(shè)置應(yīng)該大于50
        • 根據(jù)8020原則,如果80%的每秒任務(wù)數(shù)小于800,那么corePoolSize設(shè)置為80即可
      • queueCapacity = (coreSizePool/taskcost)*responsetime
        • 計(jì)算可得 queueCapacity = 80/0.1*1 = 80。意思是隊(duì)列里的線(xiàn)程可以等待1s,超過(guò)了的需要新開(kāi)線(xiàn)程來(lái)執(zhí)行
        • 切記不能設(shè)置為Integer.MAX_VALUE,這樣隊(duì)列會(huì)很大,線(xiàn)程數(shù)只會(huì)保持在corePoolSize大小,當(dāng)任務(wù)陡增時(shí),不能新開(kāi)線(xiàn)程來(lái)執(zhí)行,響應(yīng)時(shí)間會(huì)隨之陡增。
      • maxPoolSize = (max(tasks)- queueCapacity)/(1/taskcost)
        • 計(jì)算可得 maxPoolSize = (1000-80)/10 = 92
        • (最大任務(wù)數(shù)-隊(duì)列容量)/每個(gè)線(xiàn)程每秒處理能力 = 最大線(xiàn)程數(shù)
      • rejectedExecutionHandler:根據(jù)具體情況來(lái)決定,任務(wù)不重要可丟棄,任務(wù)重要?jiǎng)t要利用一些緩沖機(jī)制來(lái)處理
      • keepAliveTime和allowCoreThreadTimeout采用默認(rèn)通常能滿(mǎn)足
7. 代碼
//參數(shù)初始化
private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
//核心線(xiàn)程數(shù)量大小
private static final int corePoolSize = Math.max(2, Math.min(CPU_COUNT - 1, 4));
//線(xiàn)程池最大容納線(xiàn)程數(shù)
private static final int maximumPoolSize = CPU_COUNT * 2 + 1;
//線(xiàn)程空閑后的存活時(shí)長(zhǎng)
private static final int keepAliveTime = 30;
 
//任務(wù)過(guò)多后,存儲(chǔ)任務(wù)的一個(gè)阻塞隊(duì)列
BlockingQueue<Runnable>  workQueue = new SynchronousQueue<>();
 
//線(xiàn)程的創(chuàng)建工廠
ThreadFactory threadFactory = new ThreadFactory() {
    private final AtomicInteger mCount = new AtomicInteger(1);
 
    public Thread newThread(Runnable r) {
        return new Thread(r, "AdvacnedAsyncTask #" + mCount.getAndIncrement());
    }
};
 
//線(xiàn)程池任務(wù)滿(mǎn)載后采取的任務(wù)拒絕策略
RejectedExecutionHandler rejectHandler = new ThreadPoolExecutor.DiscardOldestPolicy();
 
//線(xiàn)程池對(duì)象,創(chuàng)建線(xiàn)程
ThreadPoolExecutor mExecute = new ThreadPoolExecutor(
        corePoolSize, //線(xiàn)程池的核心線(xiàn)程數(shù)
        maximumPoolSize,//線(xiàn)程池所能容納的最大線(xiàn)程數(shù)
        keepAliveTime,//線(xiàn)程的空閑時(shí)間
        TimeUnit.SECONDS,//keepAliveTime對(duì)應(yīng)的單位
        workQueue,//線(xiàn)程池中的任務(wù)隊(duì)列
        threadFactory, //線(xiàn)程工廠
        rejectHandler//當(dāng)任務(wù)無(wú)法被執(zhí)行時(shí)的拒絕策略
);
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。