看完這篇,再也不怕面試官問(wèn)我線程池了
本文轉(zhuǎn)載自微信公眾號(hào)「牧小農(nóng)」,作者牧小農(nóng)。轉(zhuǎn)載本文請(qǐng)聯(lián)系牧小農(nóng)公眾號(hào)。
一、為什么需要線程池
在實(shí)際使用中,線程是很占用系統(tǒng)資源的,如果對(duì)線程管理不完善的話很容易導(dǎo)致系統(tǒng)問(wèn)題。因此,在大多數(shù)并發(fā)框架中都會(huì)使用線程池來(lái)管理線程,使用線程池管理線程主要有如下好處:
- 1、使用線程池可以重復(fù)利用已有的線程繼續(xù)執(zhí)行任務(wù),避免線程在創(chuàng)建和銷毀時(shí)造成的消耗
- 2、由于沒(méi)有線程創(chuàng)建和銷毀時(shí)的消耗,可以提高系統(tǒng)響應(yīng)速度
- 3、通過(guò)線程可以對(duì)線程進(jìn)行合理的管理,根據(jù)系統(tǒng)的承受能力調(diào)整可運(yùn)行線程數(shù)量的大小等
二、工作原理
流程圖:
線程池執(zhí)行所提交的任務(wù)過(guò)程:
- 1、比如我們?cè)O(shè)置核心線程池的數(shù)量為30個(gè),不管有沒(méi)有用戶連接,我們總是保證30個(gè)連接,這個(gè)就是核心線程數(shù),這里的核心線程數(shù)不一定是30你可以根據(jù)你的需求、業(yè)務(wù)和并發(fā)訪問(wèn)量來(lái)設(shè)置,先判斷線程池中核心線程池所有的線程是否都在執(zhí)行任務(wù),如果不是,則新創(chuàng)建一個(gè)線程執(zhí)行剛提交的任務(wù),否則,核心線程池中所有的線程都在執(zhí)行任務(wù),則進(jìn)入第2步;
- 2、如果我們核心線程數(shù)的30個(gè)數(shù)量已經(jīng)滿了,就需要到阻塞隊(duì)列中去查看,判斷當(dāng)前阻塞隊(duì)列是否已滿,如果未滿,則將提交的任務(wù)放置在阻塞隊(duì)列中等待執(zhí)行;否則,則進(jìn)入第3步;
- 3、判斷線程池中所有的線程是否都在執(zhí)行任務(wù),如果沒(méi)有,則創(chuàng)建一個(gè)新的線程來(lái)執(zhí)行任務(wù),否則,則交給飽和策略進(jìn)行處理,也叫拒絕策略,等下我們會(huì)有詳細(xì)的介紹
注意: 這里有一個(gè)核心線程數(shù)和一個(gè)線程池?cái)?shù)量,這兩個(gè)是不同的概念,核心線程數(shù)代表我能夠維護(hù)常用的線程開銷,而線程池?cái)?shù)量則代表我最大能夠創(chuàng)建的線程數(shù),例如在我們農(nóng)村每家每戶都有吃水的井,基本上有半井深的水就可以維持我們的日常生活的使用,這里的半井深的水就好比我們的核心線程數(shù),還有一半的容量是我們井能夠容納的最大水資源了,超過(guò)了就不行,水就會(huì)漫出來(lái),這個(gè)就類似于我們的線程池的數(shù)量,不知道這里說(shuō)明大家是否能夠更好的進(jìn)行理解
三、線程池的分類
1.newCachedThreadPool: 創(chuàng)建一個(gè)可根據(jù)需要?jiǎng)?chuàng)建新線程的線程池,但是在以前構(gòu)造的線程可用時(shí)講重用它們,并在需要時(shí)使用提供的ThreadFactory 創(chuàng)建新線程
特征:
(1) 線程池中的數(shù)量沒(méi)有固定,可以達(dá)到最大值(Integer.MAX_VALUE=2147483647)
(2) 線程池中的線程可進(jìn)行緩存重復(fù)利用和回收(回收默認(rèn)時(shí)間為1分鐘)
(3) 當(dāng)線程池中,沒(méi)有可用線程,會(huì)重新創(chuàng)建一個(gè)線程
2.newFixedThreadPool: 創(chuàng)建一個(gè)可重用固定線程數(shù)的線程池,以共享的無(wú)界隊(duì)列方式來(lái)運(yùn)行這些線程,在任意點(diǎn),在大多數(shù)nThreads線程會(huì)處于處理任務(wù)的活動(dòng)狀態(tài)。如果在所有線程處于活動(dòng)狀態(tài)時(shí)提交附件任務(wù),則在有可用線程之前,附件任務(wù)將在隊(duì)列中等待,如果在關(guān)閉前的執(zhí)行期間由于失敗而導(dǎo)致任何線程終止,那么一個(gè)新線程將代替它執(zhí)行后續(xù)的任務(wù)(如果需要)。在某個(gè)線程被顯式關(guān)閉之前,池中的線程將一直存在
特征:
(1) 線程池中的線程處于一定的量,可以很好的控制線程的并發(fā)量
(2) 線程可以重復(fù)被使用,在顯示關(guān)閉之前,都將一直存在
(3) 超過(guò)一定量的線程被提交時(shí)需在隊(duì)列中等待
3.newSingleThreadExecutor: 創(chuàng)建一個(gè)使用單個(gè) worker 線程的Executor ,以無(wú)界隊(duì)列方式來(lái)運(yùn)行該線程。(注意,如果因?yàn)樵陉P(guān)閉前的執(zhí)行期間出現(xiàn)失敗而終止了此單個(gè)線程,那么如果需要,一個(gè)新線程將代替它執(zhí)行后續(xù)的任務(wù))。可保證順序地執(zhí)行各個(gè)任務(wù),并且在任意給定的時(shí)間不會(huì)有多個(gè)線程是活動(dòng)的,與其他等效的 newFixedThreadPool(1)不同,可保證無(wú)需重新配置此方法所返回的執(zhí)行程序即可使用其他的線程
特征:
(1) 線程池中最多執(zhí)行一個(gè)線程,之后提交的線程將會(huì)排在隊(duì)列中以此執(zhí)行
4.newSingleThreadScheduledExecutor: 創(chuàng)建一個(gè)單線程執(zhí)行程序,它可安排在給定延遲后運(yùn)行命令或者定期執(zhí)行
特征:
(1) 線程池中最多執(zhí)行一個(gè)線程,之后提交的線程活動(dòng)將會(huì)排在隊(duì)列中依次執(zhí)行
(2) 可定時(shí)或者延遲執(zhí)行線程活動(dòng)
5.newScheduledThreadPool: 創(chuàng)建一個(gè)線程池,它可安排在給定延遲后運(yùn)行命令或者定期的執(zhí)行
特征:
(1) 線程池中具有執(zhí)行數(shù)量的線程,即便是空線程也將保留
(2) 可定時(shí)或者延遲執(zhí)行線程活動(dòng)
6.newWorkStealingPool: 創(chuàng)建一個(gè)帶并行級(jí)別的線程池,并行級(jí)別決定了同一時(shí)刻最多有多少個(gè)線程在執(zhí)行,如不傳并行級(jí)別參數(shù),將默認(rèn)為當(dāng)前系統(tǒng)的CPU個(gè)數(shù)
我們可以在開發(fā)工具中搜索一個(gè)叫Executors的類,在里面我們可以看到我們上面所有的使用方法
四、ThreadPoolExecutor
線程工具類——Task :
- public class Task implements Runnable{
- @Override
- public void run() {
- try {
- //休眠1秒
- Thread.sleep(1000);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- //輸出線程名
- System.out.println(Thread.currentThread().getName()+"-------running");
- }
- }
4.1 newCachedThreadPool
源碼實(shí)現(xiàn):
- public static ExecutorService newCachedThreadPool() {
- return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
- 60L, TimeUnit.SECONDS,
- new SynchronousQueue<Runnable>());
- }
案例:
- public class CacheThreadPoolDemo {
- public static void main(String[] args) {
- ExecutorService executorService = Executors.newCachedThreadPool();
- for (int i = 0; i < 20; i++) {
- //提交任務(wù)
- executorService.execute(new Task());
- }
- //啟動(dòng)有序關(guān)閉,其中先前提交的任務(wù)將被執(zhí)行,但不會(huì)接受任何新任務(wù)
- executorService.shutdown();
- }
- }
結(jié)果輸出:
從開始到結(jié)束我們總共輸出了20個(gè)(pool-1-thread-1到pool-1-thread-20)線程
- pool-1-thread-2-------running
- pool-1-thread-6-------running
- pool-1-thread-1-------running
- pool-1-thread-3-------running
- pool-1-thread-5-------running
- pool-1-thread-4-------running
- pool-1-thread-7-------running
- pool-1-thread-11-------running
- pool-1-thread-9-------running
- pool-1-thread-10-------running
- pool-1-thread-17-------running
- pool-1-thread-15-------running
- pool-1-thread-18-------running
- pool-1-thread-16-------running
- pool-1-thread-8-------running
- pool-1-thread-20-------running
- pool-1-thread-13-------running
- pool-1-thread-19-------running
- pool-1-thread-14-------running
- pool-1-thread-12-------running
4.2 newFixedThreadPool
源碼實(shí)現(xiàn):
- public static ExecutorService newFixedThreadPool(int nThreads) {
- return new ThreadPoolExecutor(nThreads, nThreads,
- 0L, TimeUnit.MILLISECONDS,
- new LinkedBlockingQueue<Runnable>());
- }
案例:
- public class FixedThreadPoolDemo {
- public static void main(String[] args) {
- //創(chuàng)建線程池,最多允許五個(gè)線程執(zhí)行
- ExecutorService executorService = Executors.newFixedThreadPool(5);
- for (int i = 0; i < 20; i++) {
- //提交任務(wù)
- executorService.execute(new Task());
- }
- //啟動(dòng)有序關(guān)閉,其中先前提交的任務(wù)將被執(zhí)行,但不會(huì)接受任何新任務(wù)
- executorService.shutdown();
- }
- }
輸出結(jié)果:
我們可以看到其中的線程是每五個(gè)(pool-1-thread-1到pool-1-thread-5)一執(zhí)行,在當(dāng)前執(zhí)行的線程運(yùn)行中,最多允許五個(gè)線程進(jìn)行執(zhí)行
- pool-1-thread-4-------running
- pool-1-thread-2-------running
- pool-1-thread-1-------running
- pool-1-thread-3-------running
- pool-1-thread-5-------running
- pool-1-thread-4-------running
- pool-1-thread-5-------running
- pool-1-thread-3-------running
- pool-1-thread-2-------running
- pool-1-thread-1-------running
- pool-1-thread-4-------running
- pool-1-thread-2-------running
- pool-1-thread-1-------running
- pool-1-thread-3-------running
- pool-1-thread-5-------running
- pool-1-thread-4-------running
- pool-1-thread-5-------running
- pool-1-thread-2-------running
- pool-1-thread-1-------running
- pool-1-thread-3-------running
4.3 newSingleThreadExecutor
源碼實(shí)現(xiàn):
- public static ExecutorService newSingleThreadExecutor() {
- return new FinalizableDelegatedExecutorService
- (new ThreadPoolExecutor(1, 1,
- 0L, TimeUnit.MILLISECONDS,
- new LinkedBlockingQueue<Runnable>()));
- }
案例:
- public class SingleThreadPoolDemo {
- public static void main(String[] args) {
- ExecutorService executorService = Executors.newSingleThreadExecutor();
- for (int i = 0; i < 20; i++) {
- //提交任務(wù)
- executorService.execute(new Task());
- }
- //啟動(dòng)有序關(guān)閉,其中先前提交的任務(wù)將被執(zhí)行,但不會(huì)接受任何新任務(wù)
- executorService.shutdown();
- }
- }
結(jié)果輸出:
我們可以看到每次都是線程1輸出結(jié)果
- pool-1-thread-1-------running
- pool-1-thread-1-------running
- pool-1-thread-1-------running
- pool-1-thread-1-------running
- pool-1-thread-1-------running
- pool-1-thread-1-------running
- pool-1-thread-1-------running
- pool-1-thread-1-------running
- pool-1-thread-1-------running
- pool-1-thread-1-------running
- pool-1-thread-1-------running
- pool-1-thread-1-------running
- pool-1-thread-1-------running
- pool-1-thread-1-------running
- pool-1-thread-1-------running
- pool-1-thread-1-------running
- pool-1-thread-1-------running
- pool-1-thread-1-------running
- pool-1-thread-1-------running
- pool-1-thread-1-------running
五、ScheduledThreadPoolExecutor
5.1 newScheduledThreadPool
案例:
- public static void main(String[] args) {
- ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(3);
- // for (int i = 0; i < 20; i++) {
- System.out.println(System.currentTimeMillis());
- scheduledExecutorService.schedule(new Runnable() {
- @Override
- public void run() {
- System.out.println("延遲三秒執(zhí)行");
- System.out.println(System.currentTimeMillis());
- }
- },3, TimeUnit.SECONDS);
- // }
- scheduledExecutorService.shutdown();
- }
- }
輸出結(jié)果:
- 1606744468814
- 延遲三秒執(zhí)行
- 1606744471815
5.2 newSingleThreadScheduledExecutor
案例:
- public static void main(String[] args) {
- ScheduledExecutorService scheduledExecutorService = Executors.newSingleThreadScheduledExecutor();
- scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
- int i = 1;
- @Override
- public void run() {
- System.out.println(i);
- i++;
- }
- },0,1, TimeUnit.SECONDS);
- // scheduledExecutorService.shutdown();
- }
輸出結(jié)果:
- 1
- 2
- 3
- 4
- 5
六、線程池的生命周期
一般來(lái)說(shuō)線程池只有兩種狀態(tài),一種是Running,一種是TERMINATED,圖中間的都是過(guò)渡狀態(tài)
Running:能接受新提交的任務(wù),并且也能處理阻塞隊(duì)列中的任務(wù)
SHUTDOWN:關(guān)閉狀態(tài),不再接受新提交的任務(wù),但卻可以繼續(xù)處理阻塞隊(duì)列中已保存的任務(wù)
STOP:不能接受新任務(wù),也不處理隊(duì)列中的任務(wù),會(huì)中斷正在處理任務(wù)的線程
TIDYING:如果所有的任務(wù)都已終止了,workerCount(有效線程數(shù))為0.線程池進(jìn)入該狀態(tài)后會(huì)調(diào)用terminated()方法進(jìn)入TERMINATED狀態(tài)
TERMINATED:在terminated()方法執(zhí)行完成后進(jìn)入該狀態(tài),默認(rèn)terminated()方法中什么也沒(méi)有做
七、線程池的創(chuàng)建
7.1 Executors 源碼
- public ThreadPoolExecutor(int corePoolSize,
- int maximumPoolSize,
- long keepAliveTime,
- TimeUnit unit,
- BlockingQueue<Runnable> workQueue,
- ThreadFactory threadFactory,
- RejectedExecutionHandler handler)
7.2 參數(shù)說(shuō)明
corePoolSize:核心線程池的大小
maximumPoolSize:線程池能創(chuàng)建線程的最大個(gè)數(shù)
keepAliveTime:空閑線程存活時(shí)間
unit:時(shí)間單位,為keepAliveTime指定時(shí)間單位
workQueue:阻塞隊(duì)列,用于保存任務(wù)的阻塞隊(duì)列
threadFactory:創(chuàng)建線程的工程類
handler:飽和策略(拒絕策略)
八、阻塞隊(duì)列
ArrayBlockingQueue:
基于數(shù)組的阻塞隊(duì)列實(shí)現(xiàn),在ArrayBlockingQueue內(nèi)部,維護(hù)了一個(gè)定長(zhǎng)數(shù)組,以便緩存隊(duì)列中的數(shù)據(jù)對(duì)象,這是-個(gè)常用的阻塞隊(duì)列,除了一個(gè)定長(zhǎng)數(shù)組外,ArrayBlockingQueue內(nèi)部還保存著兩個(gè)整形變量,分別標(biāo)識(shí)著隊(duì)列的頭部和尾部在數(shù)組中的位置。
ArrayBlockingQueue在生產(chǎn)者放入數(shù)據(jù)和消費(fèi)者獲取數(shù)據(jù),都是共用同一個(gè)鎖對(duì)象,由此也意味著兩者無(wú)法真正并行運(yùn)行,這點(diǎn)尤其不同于LinkedBlockingQueue;按照實(shí)現(xiàn)原理來(lái)分析,ArrayBlockingQueue完全可以采用分離鎖,從而實(shí)現(xiàn)生產(chǎn)者和消費(fèi)者操作的完全并行運(yùn)行。Doug Lea之所以沒(méi)這樣去做,也許是因?yàn)锳rrayBlockingQueue的數(shù)據(jù)寫入和獲取操作已經(jīng)足夠輕巧,以至于引入獨(dú)立的鎖機(jī)制,除了給代碼帶來(lái)額外的復(fù)雜性外,其在性能上完全占不到任何便宜。
ArrayBlockingQueue和LinkedBlockingQueue間還有一個(gè)明顯的不同之處在于,前者在插入或刪除元素時(shí)不會(huì)產(chǎn)生或銷毀任何額外的對(duì)象實(shí)例,而后者則會(huì)生成一個(gè)額外的Node對(duì)象。這在長(zhǎng)時(shí)間內(nèi)需要高效并發(fā)地處理大批量數(shù)據(jù)的系統(tǒng)中,其對(duì)于GC的影響還是存在一定的區(qū)別。而在創(chuàng)建ArrayBlockingQueue時(shí),我們還可以控制對(duì)象的內(nèi)部鎖是否采用公平鎖,默認(rèn)采用非公平鎖。
LinkedBlockingQueue:
基于鏈表的阻塞隊(duì)列,同ArrayListBlockingQueue類似,其內(nèi)部也維持著一個(gè)數(shù)據(jù)緩沖隊(duì)列(該隊(duì)列由一個(gè)鏈表構(gòu)成),當(dāng)生產(chǎn)者往隊(duì)列中放入一個(gè)數(shù)據(jù)時(shí),隊(duì)列會(huì)從生產(chǎn)者手中獲取數(shù)據(jù),并緩存在隊(duì)列內(nèi)部,而生產(chǎn)者立即返回;只有當(dāng)隊(duì)列緩沖區(qū)達(dá)到最大值緩存容量時(shí)( LinkedBlockingQueue可以通過(guò)構(gòu)造函數(shù)指定該值),才會(huì)阻塞生產(chǎn)者隊(duì)列,直到消費(fèi)者從隊(duì)列中消費(fèi)掉─份數(shù)據(jù),生產(chǎn)者線程會(huì)被喚醒,反之對(duì)打于消費(fèi)者這端的處理也基于同樣的原理。而
LinkedBlockingQueue之所以能夠高效的處理并發(fā)數(shù)據(jù),還因?yàn)槠鋵?duì)于生產(chǎn)者端和消費(fèi)者端分別采用了獨(dú)立的鎖來(lái)控制數(shù)據(jù)同步,這也意味著在高并發(fā)的情況下生產(chǎn)者和消費(fèi)者可以并行地操作隊(duì)列中的數(shù)據(jù),以此來(lái)提高整個(gè)隊(duì)列的并發(fā)性能。
DelayQueue:
DelayQueue中的元素只有當(dāng)其指定的延遲時(shí)間到了,才能夠從隊(duì)列中獲取到該元素。DelayQueue是一個(gè)沒(méi)有大小限制的隊(duì)列,因此往隊(duì)列中插入數(shù)據(jù)的操作(生產(chǎn)者)永遠(yuǎn)不會(huì)被阻塞,而只有獲取數(shù)據(jù)的操作(消費(fèi)者)才會(huì)被阻塞。
使用場(chǎng)景︰
DelayQueue使用場(chǎng)景較少,但都相當(dāng)巧妙,常見(jiàn)的例子比如使用一個(gè)DelayQueue來(lái)管理一個(gè)超時(shí)未響應(yīng)的連接隊(duì)列。
PriorityBlockingQueue:
基于優(yōu)先級(jí)的阻塞隊(duì)列(優(yōu)先級(jí)的判斷通過(guò)構(gòu)造函數(shù)傳入的Compator對(duì)象來(lái)決定),但需要注意的是
PriorityBlockingQueue并不會(huì)阻塞數(shù)據(jù)生產(chǎn)者,而只會(huì)在沒(méi)有可消費(fèi)的數(shù)據(jù)時(shí),阻塞數(shù)據(jù)的消費(fèi)者。因此使用的時(shí)候要特別注意,生產(chǎn)者生產(chǎn)數(shù)據(jù)的速度絕對(duì)不能快于消費(fèi)者消費(fèi)數(shù)據(jù)的速度,否則時(shí)間一長(zhǎng),會(huì)最終耗盡所有的可用堆內(nèi)存空間。在實(shí)現(xiàn)PriorityBlockingQueue時(shí),內(nèi)部控制線程同步的鎖采用的是公平鎖。
SynchronousQueue:
一種無(wú)緩沖的等待隊(duì)列,類似于無(wú)中介的直接交易,有點(diǎn)像原始社會(huì)中的生產(chǎn)者和消費(fèi)者,生產(chǎn)者拿著產(chǎn)品去集市銷售給產(chǎn)品的最終消費(fèi)者,而消費(fèi)者必須親自去集市找到所要商品的直接生產(chǎn)者,如果一方?jīng)]有找到合適的目標(biāo),那么對(duì)不起,大家都在集市等待。相對(duì)于有緩沖的BlockingQueue來(lái)說(shuō),少了一個(gè)中間經(jīng)銷商的環(huán)節(jié)(緩沖區(qū)),如果有經(jīng)銷商,生產(chǎn)者直接把產(chǎn)品批發(fā)給經(jīng)銷商,而無(wú)需在意經(jīng)銷商最終會(huì)將這些產(chǎn)品賣給那些消費(fèi)者,由于經(jīng)銷商可以庫(kù)存一部分商品,因此相對(duì)于直接交易模式,總體來(lái)說(shuō)采用中間經(jīng)銷商的模式會(huì)吞吐量高一些(可以批量買賣)﹔但另一方面,又因?yàn)榻?jīng)銷商的引入,使得產(chǎn)品從生產(chǎn)者到消費(fèi)者中間增加了額外的交易環(huán)節(jié),單個(gè)產(chǎn)品的及時(shí)響應(yīng)性能可能會(huì)降低。
聲明一個(gè)SynchronousQueue有兩種不同的方式,它們之間有著不太一樣的行為。公平模式和非公平模式的區(qū)別:如果采用公平模式:SynchronousQueue會(huì)采用公平鎖,并配合一個(gè)FIFO隊(duì)列來(lái)阻塞多余的生產(chǎn)者和消費(fèi)者,從而體系整體的公平策略;
但如果是非公平模式 ( SynchronousQueue默認(rèn)) : SynchronousQueue采用非公平鎖,同時(shí)配合一個(gè)LIFO隊(duì)列來(lái)管理多余的生產(chǎn)者和消費(fèi)者,而后一種模式,如果生產(chǎn)者和消費(fèi)者的處理速度有差距,則很容易出現(xiàn)饑渴的情況,即可能有某些生產(chǎn)者或者是消費(fèi)者的數(shù)據(jù)永遠(yuǎn)都得不到處理。
注意:
arrayblockingqueue和linkedblockqueue的區(qū)別:1.隊(duì)列中鎖的實(shí)現(xiàn)不同
1、ArrayBlockingQueue實(shí)現(xiàn)的隊(duì)列中的鎖是沒(méi)有分離的,即生產(chǎn)和消費(fèi)用的是同一個(gè)鎖;
LinkedBlockingQueue實(shí)現(xiàn)的隊(duì)列中的鎖是分離的,即生產(chǎn)用的是putLock,消費(fèi)是takeLock2.隊(duì)列大小初始化方式不同
2、ArrayBlockingQueue實(shí)現(xiàn)的隊(duì)列中必須指定隊(duì)列的大小;
LinkedBlockingQueue實(shí)現(xiàn)的隊(duì)列中可以不指定隊(duì)列的大小,但是默認(rèn)是Integer.MAX_VALUE
九、拒絕策略
ThreadPoolExecutor.AbortPolicy(系統(tǒng)默認(rèn)):丟棄任務(wù)并拋出RejectedExecutionException異常,讓你感知到任務(wù)被拒絕了,我們可以根據(jù)業(yè)務(wù)邏輯選擇重試或者放棄提交等策略
ThreadPoolExecutor.DiscardPolicy: 也是丟棄任務(wù),但是不拋出異常,相對(duì)而言存在一定的風(fēng)險(xiǎn),因?yàn)槲覀兲峤坏臅r(shí)候根本不知道這個(gè)任務(wù)會(huì)被丟棄,可能造成數(shù)據(jù)丟失。
ThreadPoolExecutor.DiscardOldestPolicy: 丟棄隊(duì)列最前面的任務(wù),然后重新嘗試執(zhí)行任務(wù)(重復(fù)此過(guò)程),通常是存活時(shí)間最長(zhǎng)的任務(wù),它也存在一定的數(shù)據(jù)丟失風(fēng)險(xiǎn)
ThreadPoolExecutor.CallerRunsPolicy: 由調(diào)用線程處理該任務(wù)
十、execute()和submit()方法
10.1 execute方法執(zhí)行邏輯
- 如果當(dāng)前運(yùn)行的線程少于corePoolSize,則會(huì)創(chuàng)建新的線程來(lái)執(zhí)行新的任務(wù);
- 如果運(yùn)行的線程個(gè)數(shù)等于或者大于corePoolSize,則會(huì)將提交的任務(wù)存放到阻塞隊(duì)列workQueue中;
- 如果當(dāng)前workQueue隊(duì)列已滿的話,則會(huì)創(chuàng)建新的線程來(lái)執(zhí)行任務(wù);
- 如果線程個(gè)數(shù)已經(jīng)超過(guò)了maximumPoolSize,則會(huì)使用飽和策略RejectedExecutionHandler來(lái)進(jìn)行處理
10.2 Submit
submit是基方法Executor.execute(Runnable)的延伸,通過(guò)創(chuàng)建并返回一個(gè)Future類對(duì)象可用于取消執(zhí)行和/或等待完成。
十一、線程池的關(guān)閉
- 關(guān)閉線程池,可以通過(guò)shutdown和shutdownNow兩個(gè)方法
- 原理:遍歷線程池中的所有線程,然后依次中斷
- 1、shutdownNow首先將線程池的狀態(tài)設(shè)置為STOP,然后嘗試停止所有的正在執(zhí)行和未執(zhí)行任務(wù)的線程,并返回等待執(zhí)行任務(wù)的列表;
- 2、shutdown只是將線程池的狀態(tài)設(shè)置為SHUTDOWN狀態(tài),然后中斷所有沒(méi)有正在執(zhí)行任務(wù)的線程