什么是線程池
線程池可以通過池看出來是一個(gè)資源集解取,任何池的作用都大同小異,主要是用來減少資源創(chuàng)建拔疚、初始化的系統(tǒng)開銷。
創(chuàng)建線程很“貴”嗎
是的既荚。創(chuàng)建線程的代價(jià)是昂貴的草雕。
我們都知道系統(tǒng)中的每個(gè)進(jìn)程有自己獨(dú)立的內(nèi)存空間,而被稱為輕量級(jí)進(jìn)程的線程也是需要的固以。
在JVM中默認(rèn)一個(gè)線程需要使用256k~1M(取決于32位還是64位操作系統(tǒng))的內(nèi)存墩虹。(具體的數(shù)組我們不深究,因?yàn)殡S著JVM版本的變化這個(gè)默認(rèn)值隨時(shí)可能發(fā)生變更憨琳,我們只需要知道線程是需要占用內(nèi)存的)
許多文章會(huì)將上下文切換诫钓、CPU調(diào)度列入其中,這邊不將線程調(diào)度列入是因?yàn)樗咧械木€程不會(huì)被調(diào)度(OS控制)篙螟,如果不是睡眠中的線程那么是一定需要被調(diào)度的菌湃。
但在JVM中除了創(chuàng)建時(shí)的內(nèi)存消耗,還會(huì)給GC帶來壓力遍略,如果頻繁創(chuàng)建線程那么相對(duì)的GC的時(shí)候也需要回收對(duì)應(yīng)的線程惧所。
線程池的機(jī)制
可以看到線程池是一種重復(fù)利用線程的技術(shù),線程池的主要機(jī)制就是保留一定的線程數(shù)在沒有事情做的時(shí)候使之睡眠绪杏,當(dāng)有活干的時(shí)候拿一個(gè)線程去運(yùn)行下愈。
這些牽扯到線程池實(shí)現(xiàn)的具體策略。
線程池的好處
第一:降低資源消耗蕾久。通過重復(fù)利用已創(chuàng)建的線程降低線程創(chuàng)建和銷毀造成的消耗势似。
第二:提高響應(yīng)速度。當(dāng)任務(wù)到達(dá)時(shí)僧著,任務(wù)可以不需要的等到線程創(chuàng)建就能立即執(zhí)行履因。
第三:提高線程的可管理性。線程是稀缺資源盹愚,如果無限制的創(chuàng)建栅迄,不僅會(huì)消耗系統(tǒng)資源,還會(huì)降低系統(tǒng)的穩(wěn)定性皆怕,使用線程池可以進(jìn)行統(tǒng)一的分配毅舆,調(diào)優(yōu)和監(jiān)控。但是要做到合理的利用線程池端逼,必須對(duì)其原理了如指掌
線程池的主要工作流程
從上圖我們可以看出朗兵,當(dāng)提交一個(gè)新任務(wù)到線程池時(shí),線程池的處理流程如下:
首先線程池判斷基本線程池是否已滿顶滩?沒滿,創(chuàng)建一個(gè)工作線程來執(zhí)行任務(wù)寸爆。滿了礁鲁,則進(jìn)入下個(gè)流程盐欺。
其次線程池判斷工作隊(duì)列是否已滿?沒滿仅醇,則將新提交的任務(wù)存儲(chǔ)在工作隊(duì)列里冗美。滿了,則進(jìn)入下個(gè)流程析二。
最后線程池判斷整個(gè)線程池是否已滿粉洼?沒滿,則創(chuàng)建一個(gè)新的工作線程來執(zhí)行任務(wù)叶摄,滿了属韧,則交給飽和策略來處理這個(gè)任務(wù)。
線程池的創(chuàng)建
我們可以通過ThreadPoolExecutor來創(chuàng)建一個(gè)線程池蛤吓。
new ThreadPoolExecutor(corePoolSize, maximumPoolSize,
keepAliveTime, milliseconds,runnableTaskQueue, threadFactory,handler);
創(chuàng)建一個(gè)線程池需要輸入幾個(gè)參數(shù):
1宵喂、corePoolSize - 線程池核心池的大小。
2会傲、maximumPoolSize - 線程池的最大線程數(shù)锅棕。
3、keepAliveTime - 當(dāng)線程數(shù)大于核心時(shí)淌山,此為終止前多余的空閑線程等待新任務(wù)的最長(zhǎng)時(shí)間裸燎。
4、unit - keepAliveTime 的時(shí)間單位泼疑。
5顺少、workQueue - 用來儲(chǔ)存等待執(zhí)行任務(wù)的隊(duì)列。
6王浴、threadFactory - 線程工廠脆炎。
7、handler - 拒絕策略氓辣。
corePoolSize:核心池的大小秒裕,這個(gè)參數(shù)跟后面講述的線程池的實(shí)現(xiàn)原理有非常大的關(guān)系。在創(chuàng)建了線程池后钞啸,默認(rèn)情況下几蜻,線程池中并沒有任何線程,而是等待有任務(wù)到來才創(chuàng)建線程去執(zhí)行任務(wù)体斩,除非調(diào)用了prestartAllCoreThreads()或者prestartCoreThread()方法梭稚,從這2個(gè)方法的名字就可以看出,是預(yù)創(chuàng)建線程的意思絮吵,即在沒有任務(wù)到來之前就創(chuàng)建corePoolSize個(gè)線程或者一個(gè)線程弧烤。默認(rèn)情況下,在創(chuàng)建了線程池后蹬敲,線程池中的線程數(shù)為0暇昂,當(dāng)有任務(wù)來之后莺戒,就會(huì)創(chuàng)建一個(gè)線程去執(zhí)行任務(wù),當(dāng)線程池中的線程數(shù)目達(dá)到corePoolSize后急波,就會(huì)把到達(dá)的任務(wù)放到緩存隊(duì)列當(dāng)中从铲;
maximumPoolSize:線程池最大線程數(shù),這個(gè)參數(shù)也是一個(gè)非常重要的參數(shù)澄暮,它表示在線程池中最多能創(chuàng)建多少個(gè)線程名段;
keepAliveTime:表示線程沒有任務(wù)執(zhí)行時(shí)最多保持多久時(shí)間會(huì)終止。默認(rèn)情況下泣懊,只有當(dāng)線程池中的線程數(shù)大于corePoolSize時(shí)伸辟,keepAliveTime才會(huì)起作用,直到線程池中的線程數(shù)不大于corePoolSize嗅定,即當(dāng)線程池中的線程數(shù)大于corePoolSize時(shí)自娩,如果一個(gè)線程空閑的時(shí)間達(dá)到keepAliveTime,則會(huì)終止渠退,直到線程池中的線程數(shù)不超過corePoolSize忙迁。但是如果調(diào)用了allowCoreThreadTimeOut(boolean)方法,在線程池中的線程數(shù)不大于corePoolSize時(shí)碎乃,keepAliveTime參數(shù)也會(huì)起作用姊扔,直到線程池中的線程數(shù)為0;
unit:參數(shù)keepAliveTime的時(shí)間單位梅誓,有7種取值恰梢。TimeUnit.DAYS、TimeUnit.HOURS梗掰、TimeUnit.MINUTES嵌言、TimeUnit.SECONDS、TimeUnit.MILLISECONDS及穗、TimeUnit.MICROSECONDS摧茴、TimeUnit.NANOSECONDS
workQueue:一個(gè)阻塞隊(duì)列,用來存儲(chǔ)等待執(zhí)行的任務(wù)埂陆,這個(gè)參數(shù)的選擇也很重要苛白,會(huì)對(duì)線程池的運(yùn)行過程產(chǎn)生重大影響,一般來說焚虱,這里的阻塞隊(duì)列有以下幾種選擇:ArrayBlockingQueue购裙、LinkedBlockingQueue、SynchronousQueue鹃栽。?
ArrayBlockingQueue和PriorityBlockingQueue使用較少躏率,一般使用LinkedBlockingQueue和SynchronousQueue。線程池的排隊(duì)策略與BlockingQueue有關(guān)。
threadFactory:線程工廠禾锤,主要用來創(chuàng)建線程私股;
handler:表示當(dāng)拒絕處理任務(wù)時(shí)的策略摹察,有以下四種取值:?
ThreadPoolExecutor.AbortPolicy:丟棄任務(wù)并拋出RejectedExecutionException異常恩掷。?
ThreadPoolExecutor.DiscardPolicy:也是丟棄任務(wù),但是不拋出異常供嚎。?
ThreadPoolExecutor.DiscardOldestPolicy:丟棄隊(duì)列最前面的任務(wù)黄娘,然后重新嘗試執(zhí)行任務(wù)(重復(fù)此過程)?
ThreadPoolExecutor.CallerRunsPolicy:由調(diào)用線程處理該任務(wù)
當(dāng)然也可以根據(jù)應(yīng)用場(chǎng)景需要來實(shí)現(xiàn)RejectedExecutionHandler接口自定義策略。如記錄日志或持久化不能處理的任務(wù)克滴。
線程池不允許使用Executors去創(chuàng)建
線程池不允許使用Executors去創(chuàng)建逼争,而是通過ThreadPoolExecutor的方式,這樣的處理方式讓寫的同學(xué)更加明確線程池的運(yùn)行規(guī)則劝赔,規(guī)避資源耗盡的風(fēng)險(xiǎn)誓焦。 說明:Executors各個(gè)方法的弊端:
1)newFixedThreadPool和newSingleThreadExecutor
主要問題是堆積的請(qǐng)求處理隊(duì)列可能會(huì)耗費(fèi)非常大的內(nèi)存,甚至OOM着帽。
newSingleThreadExecutor
創(chuàng)建一個(gè)單線程的線程池杂伟。這個(gè)線程池只有一個(gè)線程在工作,也就是相當(dāng)于單線程串行執(zhí)行所有任務(wù)仍翰。如果這個(gè)唯一的線程因?yàn)楫惓=Y(jié)束赫粥,那么會(huì)有一個(gè)新的線程來替代它。
此線程池保證所有任務(wù)的執(zhí)行順序予借,按照任務(wù)的提交順序(FIFO, LIFO, 優(yōu)先級(jí))執(zhí)行越平。
newFixedThreadPool
創(chuàng)建固定大小的線程池。每次提交一個(gè)任務(wù)就創(chuàng)建一個(gè)線程灵迫,直到線程達(dá)到線程池的最大大小秦叛。
線程池的大小一旦達(dá)到最大值就會(huì)保持不變,如果某個(gè)線程因?yàn)閳?zhí)行異常而結(jié)束瀑粥,那么線程池會(huì)補(bǔ)充一個(gè)新線程挣跋。
可控制線程最大并發(fā)數(shù),超出的線程會(huì)在隊(duì)列中等待
2)newCachedThreadPool和newScheduledThreadPool
主要問題是線程數(shù)最大數(shù)是Integer.MAX_VALUE利凑,可能會(huì)創(chuàng)建數(shù)量非常多的線程浆劲,甚至OOM。
newCachedThreadPool
創(chuàng)建一個(gè)可緩存的線程池哀澈。如果線程池的大小超過了處理任務(wù)所需要的線程牌借,
那么就會(huì)回收部分空閑(60秒不執(zhí)行任務(wù))的線程,當(dāng)任務(wù)數(shù)增加時(shí)割按,此線程池又可以智能的添加新線程來處理任務(wù)膨报。
此線程池不會(huì)對(duì)線程池大小做限制
線程池大小完全依賴于操作系統(tǒng)(或者說JVM)能夠創(chuàng)建的最大線程大小。
newScheduledThreadPool
創(chuàng)建一個(gè)定時(shí)線程池,支持定時(shí)及周期性任務(wù)執(zhí)行
線程池創(chuàng)建方式(推薦)
根據(jù)阿里巴巴java開發(fā)規(guī)范现柠,推薦了3種線程池創(chuàng)建方式
推薦方式1:
首先引入:commons-lang3包
推薦方式 2:
首先引入:com.google.guava包
推薦方式 3:
spring配置線程池方式:自定義線程工廠bean需要實(shí)現(xiàn)ThreadFactory院领,可參考該接口的其它默認(rèn)實(shí)現(xiàn)類,使用方式直接注入bean
調(diào)用execute(Runnable task)方法即可
線程使用示例
創(chuàng)建線程工廠方法
package com.guo.test.app.common.util
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import java.util.concurrent.*;
public class AsynExecutorUtil {
public static final ThreadFactoryNAMED_THREAD_FACTORY =new ThreadFactoryBuilder().setNameFormat("test_asyn_executor").build();
? ? public static final ExecutorServiceEXECUTOR? ? ? ? ? ? =new ThreadPoolExecutor(10, 20, 0L,
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? TimeUnit.MILLISECONDS,
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? new LinkedBlockingQueue(1024),
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? NAMED_THREAD_FACTORY,
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? new ThreadPoolExecutor.AbortPolicy());
}
異步使用線程:
AsynExecutorUtil.EXECUTOR.submit(()->testBO.testAsynExecutor(id));
參考:
https://www.cnblogs.com/ants/p/11343657.html
https://www.toutiao.com/i6739759947166253580/
https://blog.csdn.net/weixin_41888813/article/details/90769126