Java并發(fā)——ThreadPoolExecutor線程池解析及Executor創(chuàng)建線程常見四種方式

前言:

在剛學(xué)Java并發(fā)的時候基本上第一個demo都會寫new Thread來創(chuàng)建線程崇败。但是隨著學(xué)的深入之后發(fā)現(xiàn)基本上都是使用線程池來直接獲取線程挖藏。那么為什么會有這樣的情況發(fā)生呢锅论?

new Thread和線程池的比較

每次new Thread是新建了線程對象绿店,并且不能重復(fù)使用璃搜,為什么不能重復(fù)使用拙寡?因?yàn)閚ew是相當(dāng)于在內(nèi)存中獨(dú)立開辟一個內(nèi)存來讓該線程運(yùn)行授滓,所以只能釋放線程資源和新建線程,性能差肆糕。而使用線程池般堆,可以重復(fù)使用存在的線程,減少對象的創(chuàng)建诚啃,消亡的開銷淮摔,性能較好

線程缺乏統(tǒng)一管理,可能無限制的新建線程始赎,相互競爭和橙,有可能占用過多的系統(tǒng)資源導(dǎo)致死機(jī)或者拋出OutOfMemoryError。而使用線程池可以有效控制最大并發(fā)線程數(shù)造垛,提高系統(tǒng)資源利用率魔招,同時避免過多資源競爭,避免阻塞

同時new Thread筋搏,當(dāng)我們需要定期執(zhí)行仆百,更多執(zhí)行,線程中斷等等使用Thread操作起來非常的繁瑣奔脐。線程池則提供定時執(zhí)行俄周,定期執(zhí)行,單線程髓迎,并發(fā)控制等功能峦朗,讓我們操作線程起來特別方便

進(jìn)群:697699179可以獲取Java各類入門學(xué)習(xí)資料!

這是我的微信公眾號【編程study】各位大佬有空可以關(guān)注下排龄,每天更新Java學(xué)習(xí)方法波势,感謝!

學(xué)習(xí)中遇到問題有不明白的地方橄维,推薦加小編Java學(xué)習(xí)群:697699179內(nèi)有視頻教程 尺铣,直播課程 ,等學(xué)習(xí)資料争舞,期待你的加入

ThreadPoolExecutor如何創(chuàng)建對象

在這里介紹的是JUC包下的ThreadPoolExecutor線程池凛忿,這個線程池里有4個構(gòu)造方法

publicclassThreadPoolExecutorextendsAbstractExecutorService{//第一個構(gòu)造方法publicThreadPoolExecutor(intcorePoolSize,intmaximumPoolSize,longkeepAliveTime,? ? ? ? ? ? ? ? ? ? ? ? ? ? ? TimeUnit unit,? ? ? ? ? ? ? ? ? ? ? ? ? ? ? BlockingQueue workQueue){this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,? ? ? ? ? ? Executors.defaultThreadFactory(), defaultHandler);? ? }//第二個構(gòu)造方法publicThreadPoolExecutor(intcorePoolSize,intmaximumPoolSize,longkeepAliveTime,? ? ? ? ? ? ? ? ? ? ? ? ? ? ? TimeUnit unit,? ? ? ? ? ? ? ? ? ? ? ? ? ? ? BlockingQueue workQueue,? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ThreadFactory threadFactory){this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,? ? ? ? ? ? threadFactory, defaultHandler);? ? }//第三個構(gòu)造方法publicThreadPoolExecutor(intcorePoolSize,intmaximumPoolSize,longkeepAliveTime,? ? ? ? ? ? ? ? ? ? ? ? ? ? ? TimeUnit unit,? ? ? ? ? ? ? ? ? ? ? ? ? ? ? BlockingQueue workQueue,? ? ? ? ? ? ? ? ? ? ? ? ? ? ? RejectedExecutionHandler handler){this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,? ? ? ? ? ? Executors.defaultThreadFactory(), handler);? ? }//第四個也是真正的初始化構(gòu)造函數(shù)publicThreadPoolExecutor(intcorePoolSize,intmaximumPoolSize,longkeepAliveTime,? ? ? ? ? ? ? ? ? ? ? ? ? ? ? TimeUnit unit,? ? ? ? ? ? ? ? ? ? ? ? ? ? ? BlockingQueue workQueue,? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ThreadFactory threadFactory,? ? ? ? ? ? ? ? ? ? ? ? ? ? ? RejectedExecutionHandler handler){if(corePoolSize <0||? ? ? ? ? ? maximumPoolSize <=0||? ? ? ? ? ? maximumPoolSize < corePoolSize ||? ? ? ? ? ? keepAliveTime <0)thrownewIllegalArgumentException();if(workQueue ==null|| threadFactory ==null|| handler ==null)thrownewNullPointerException();this.corePoolSize = corePoolSize;this.maximumPoolSize = maximumPoolSize;this.workQueue = workQueue;this.keepAliveTime = unit.toNanos(keepAliveTime);this.threadFactory = threadFactory;this.handler = handler;? ? }}

從這四個函數(shù)來看,其實(shí)是分是否需要默認(rèn)的線程池工廠和handler竞川。接下來就講講這些參數(shù)代表什么店溢。

corePoolSize:核心線程數(shù)量。當(dāng)線程數(shù)少于corePoolSize的時候委乌,直接創(chuàng)建新的線程床牧,盡管其他線程是空閑的。當(dāng)線程池中的線程數(shù)目達(dá)到?corePoolSize后遭贸,就會把到達(dá)的任務(wù)放到緩存隊(duì)列當(dāng)中;

maximunPoolSize:線程池最大線程數(shù)戈咳。如果線程數(shù)量少于線程最大數(shù)且大于核心線程數(shù)量的時候,只有當(dāng)阻塞隊(duì)列滿了才創(chuàng)建新線程壕吹。當(dāng)線程數(shù)量大于最大線程數(shù)且阻塞隊(duì)列滿了這時候就會執(zhí)行一些策略來響應(yīng)該線程除秀。

workQueue:阻塞隊(duì)列,存儲等待執(zhí)行的任務(wù)算利,會對線程池的運(yùn)行產(chǎn)生很大的影響册踩。當(dāng)提交一個新的任務(wù)到線程池的時候,線程池會根據(jù)當(dāng)前線程數(shù)量來選擇不同的處理方式

直接切換隊(duì)列SynchronousQueue:該隊(duì)列傳遞任務(wù)到線程而不持有它們效拭。在這一點(diǎn)上暂吉,試圖向該隊(duì)列壓入一個任務(wù),如果沒有可用的線程立刻運(yùn)行任務(wù)缎患,那么就會入列失敗慕的,所以一個新的線程就會被創(chuàng)建。當(dāng)處理那些內(nèi)部依賴的任務(wù)集合時挤渔,這個選擇可以避免鎖住肮街。直接接傳遞通常需要無邊界的最大線程數(shù)來避免新提交任務(wù)被拒絕處理。當(dāng)任務(wù)以平均快于被處理的速度提交到線程池時判导,它依次地確認(rèn)無邊界線程增長的可能性

使用無界隊(duì)列LinkedBlockingQueue:使用這個隊(duì)列的話嫉父,沒有預(yù)先定義容量的無界隊(duì)列沛硅,最大線程數(shù)是為corePoolSize,在核心線程都繁忙的時候會使新提交的任務(wù)在隊(duì)列中等待被執(zhí)行绕辖,所以將不會創(chuàng)建更多的線程摇肌,這時候,maximunPoolSize最大線程數(shù)的值將不起作用仪际。當(dāng)每個任務(wù)之間是相互獨(dú)立的時比較適合該隊(duì)列围小,任務(wù)之間不能互相影響執(zhí)行。

使用有界隊(duì)列ArrayBlockingQueue:使用這個隊(duì)列树碱,線程池中的最大線程數(shù)量就是maximunPoolSize肯适,能夠降低資源消耗,但是卻使得線程之間調(diào)度變得更加困難成榜,因?yàn)殛?duì)列容量和線程池都規(guī)定完了框舔。

如果想降低系統(tǒng)資源消耗,包括CPU使用率伦连,操作系統(tǒng)資源消耗雨饺,上下文切換開銷等等,可以設(shè)置一個較大的隊(duì)列容量惑淳,較小的maximunPoolSize额港。如果線程經(jīng)常發(fā)生阻塞,那么可以稍微將maximunPoolSize設(shè)置大一點(diǎn)

keepAliveTime:線程沒有任務(wù)執(zhí)行最多保持多久時間終止歧焦。也就是當(dāng)線程數(shù)量超過核心線程數(shù)量的時候移斩,且小于最大線程數(shù)量,這一部分的線程在沒有任務(wù)執(zhí)行的時候是會保持直到超過keepAliveTime才會銷毀

unit:keepAliveTime的時間單位

threadFactory:線程工廠绢馍,用來創(chuàng)建線程向瓷,當(dāng)使用默認(rèn)的線程工廠創(chuàng)建線程的時候,會使得線程具有相同優(yōu)先級舰涌,并且設(shè)置了守護(hù)性猖任,同時也設(shè)置線程名稱

handler:拒絕策略。當(dāng)workQueue滿了瓷耙,并且沒有空閑的線程數(shù)朱躺,即線程達(dá)到最大線程數(shù)。就會有四種不同策略來處理

直接拋出異常(默認(rèn))

使用調(diào)用者所在線程執(zhí)行任務(wù)

丟棄隊(duì)列中最靠前的任務(wù)搁痛,并執(zhí)行當(dāng)前任務(wù)

直接丟棄當(dāng)前任務(wù)

這四種就是對應(yīng)的策略實(shí)現(xiàn)類(從上到下)长搀,是在ThreadPoolExecutor中的內(nèi)部類

線程池五種狀態(tài)

RUNNING:在這個狀態(tài)的線程池能判斷接受新提交的任務(wù),并且也能處理阻塞隊(duì)列中的任務(wù)

SHUTDOWN:處于關(guān)閉的狀態(tài)鸡典,該線程池不能接受新提交的任務(wù)源请,但是可以處理阻塞隊(duì)列中已經(jīng)保存的任務(wù),在線程處于RUNNING狀態(tài),調(diào)用shutdown()方法能切換為該狀態(tài)谁尸。

STOP:線程池處于該狀態(tài)時既不能接受新的任務(wù)也不能處理阻塞隊(duì)列中的任務(wù)舅踪,并且能中斷現(xiàn)在線程中的任務(wù)。當(dāng)線程處于RUNNING和SHUTDOWN狀態(tài)症汹,調(diào)用shutdownNow()方法就可以使線程變?yōu)樵摖顟B(tài)

TIDYING:在SHUTDOWN狀態(tài)下阻塞隊(duì)列為空硫朦,且線程中的工作線程數(shù)量為0就會進(jìn)入該狀態(tài)贷腕,當(dāng)在STOP狀態(tài)下時背镇,只要線程中的工作線程數(shù)量為0就會進(jìn)入該狀態(tài)。

TERMINATED:在TIDYING狀態(tài)下調(diào)用terminated()方法就會進(jìn)入該狀態(tài)泽裳÷髡叮可以認(rèn)為該狀態(tài)是最終的終止?fàn)顟B(tài)。

execute()方法解析(JDK1.8)

execute():提交任務(wù)交給線程池運(yùn)行

publicclassThreadPoolExecutorextendsAbstractExecutorService{? ? public void execute(Runnablecommand) {//任務(wù)為空則報空異常if(command ==null)thrownewNullPointerException();/*

? ? ? ? * 1. 如果正在運(yùn)行的線程數(shù)少于corePoolSize涮总。那么就嘗試去開始一個新線程并用傳入的command作為它第一個任務(wù)胸囱,

      * 然后讓addworker去原子性的檢查線程池的運(yùn)行狀態(tài)和線程數(shù)量,以至于能提前知道是否能添加線程進(jìn)去瀑梗。

      * 如果成功則線程運(yùn)行烹笔,不成功就會去到下一步,并且再獲取一次ctl的值(ctl是用來獲取線程狀態(tài)和線程數(shù)的)

? ? ? ? */int c = ctl.get();if(workerCountOf(c) < corePoolSize) {if(addWorker(command,true))return;? ? ? ? ? ? c = ctl.get();? ? ? ? }/*

? ? ? ? * 2.如果一個任務(wù)能被成功的排隊(duì)抛丽,那么仍然需要雙重檢查是否我們需要添加一個新的線程(因?yàn)橛锌赡軙谝淮螜z查完后有一個線程銷毀谤职,所以需要雙重檢查)

      * 或者進(jìn)入此方法后線程關(guān)閉。所以很有必要重新檢查一遍狀態(tài)是否需要回滾排隊(duì)亿鲜,是否停止或開始一個新線程或是否不存在

? ? ? ? */if(isRunning(c) && workQueue.offer(command)) {? ? ? ? ? ? int recheck = ctl.get();if(! isRunning(recheck) && remove(command))? ? ? ? ? ? ? ? reject(command);elseif(workerCountOf(recheck) ==0)? ? ? ? ? ? ? ? addWorker(null,false);? ? ? ? }/*

? ? ? ? ? *3.如果我們無法將任務(wù)進(jìn)行排隊(duì)(即進(jìn)入隊(duì)列中)允蜈,那么我們嘗試添加一個新線程,如果失敗我們就使用拒絕策略拒絕這個任務(wù)

? ? ? ? ? */elseif(!addWorker(command,false))? ? ? ? ? ? reject(command);? ? }}

線程池的一些常用方法

submit():提交任務(wù)蒿柳,能夠返回執(zhí)行結(jié)果execute+Future

shutdown():關(guān)閉線程池饶套,等待任務(wù)都執(zhí)行完

shutdownNow():關(guān)閉線程池,不等待任務(wù)執(zhí)行完

getTaskCount():線程池已執(zhí)行和未執(zhí)行的任務(wù)總數(shù)

getCompletedTaskCount():已完成的任務(wù)數(shù)量

getPoolSize():線程池當(dāng)前線程數(shù)量

getActiveCount():當(dāng)前線程池中正在執(zhí)行任務(wù)的線程數(shù)量

Executor線程池創(chuàng)建的四種線程

newFixedThreadPool:創(chuàng)建的是定長的線程池垒探,可以控制線程最大并發(fā)數(shù)妓蛮,超出的線程會在線程中等待,使用的是無界隊(duì)列圾叼,核心線程數(shù)和最大線程數(shù)一樣蛤克,當(dāng)線程池中的線程沒有任務(wù)時候立刻銷毀,使用默認(rèn)線程工廠。

newSingleThreadExecutor:創(chuàng)建的是單線程化的線程池褐奥,只會用唯一一個工作線程執(zhí)行任務(wù)咖耘,可以指定按照是否是先入先出,還是優(yōu)先級來執(zhí)行任務(wù)撬码。同樣使用無界隊(duì)列儿倒,核心線程數(shù)和最大線程數(shù)都是1個,同樣keepAliveTime為0,可選擇是否使用默認(rèn)線程工廠夫否。

newCachedThreadPool:設(shè)定一個可緩存的線程池彻犁,當(dāng)線程池長度超過處理的需要,可以靈活回收空閑線程凰慈,如果沒有可以回收的才新建線程汞幢。沒有核心線程數(shù),當(dāng)線程沒有任務(wù)60s之后就會回收空閑線程微谓,使用有界隊(duì)列森篷。同樣可以選擇是否使用默認(rèn)線程工廠。

newScheduledThreadPool:支持線程定時操作和周期性操作豺型。

下面是方法的源碼:

publicclassExecutors{publicstaticExecutorServicenewFixedThreadPool(intnThreads){returnnewThreadPoolExecutor(nThreads, nThreads,0L, TimeUnit.MILLISECONDS,newLinkedBlockingQueue());? ? }publicstaticExecutorServicenewSingleThreadExecutor(){returnnewFinalizableDelegatedExecutorService? ? ? ? ? ? (newThreadPoolExecutor(1,1,0L, TimeUnit.MILLISECONDS,newLinkedBlockingQueue()));? ? }publicstaticExecutorServicenewSingleThreadExecutor(ThreadFactory threadFactory){returnnewFinalizableDelegatedExecutorService? ? ? ? ? ? (newThreadPoolExecutor(1,1,0L, TimeUnit.MILLISECONDS,newLinkedBlockingQueue(),? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? threadFactory));? ? }publicstaticExecutorServicenewCachedThreadPool(){returnnewThreadPoolExecutor(0, Integer.MAX_VALUE,60L, TimeUnit.SECONDS,newSynchronousQueue());? ? }publicstaticExecutorServicenewCachedThreadPool(ThreadFactory threadFactory){returnnewThreadPoolExecutor(0, Integer.MAX_VALUE,60L, TimeUnit.SECONDS,newSynchronousQueue(),? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? threadFactory);? ? }publicstaticScheduledExecutorServicenewScheduledThreadPool(intcorePoolSize){returnnewScheduledThreadPoolExecutor(corePoolSize);? ? }publicstaticScheduledExecutorServicenewScheduledThreadPool(intcorePoolSize, ThreadFactory threadFactory){returnnewScheduledThreadPoolExecutor(corePoolSize, threadFactory);? ? }}

intc= ctl.get();if(workerCountOf(c) < corePoolSize) {if(addWorker(command,true))return;c= ctl.get();}

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末仲智,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子姻氨,更是在濱河造成了極大的恐慌钓辆,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,734評論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件肴焊,死亡現(xiàn)場離奇詭異前联,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)娶眷,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,931評論 3 394
  • 文/潘曉璐 我一進(jìn)店門似嗤,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人茂浮,你說我怎么就攤上這事双谆。” “怎么了席揽?”我有些...
    開封第一講書人閱讀 164,133評論 0 354
  • 文/不壞的土叔 我叫張陵顽馋,是天一觀的道長。 經(jīng)常有香客問我幌羞,道長寸谜,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,532評論 1 293
  • 正文 為了忘掉前任属桦,我火速辦了婚禮熊痴,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘聂宾。我一直安慰自己果善,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,585評論 6 392
  • 文/花漫 我一把揭開白布系谐。 她就那樣靜靜地躺著巾陕,像睡著了一般讨跟。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上鄙煤,一...
    開封第一講書人閱讀 51,462評論 1 302
  • 那天晾匠,我揣著相機(jī)與錄音,去河邊找鬼梯刚。 笑死凉馆,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的亡资。 我是一名探鬼主播澜共,決...
    沈念sama閱讀 40,262評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼沟于!你這毒婦竟也來了咳胃?” 一聲冷哼從身側(cè)響起植康,我...
    開封第一講書人閱讀 39,153評論 0 276
  • 序言:老撾萬榮一對情侶失蹤旷太,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后销睁,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體供璧,經(jīng)...
    沈念sama閱讀 45,587評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,792評論 3 336
  • 正文 我和宋清朗相戀三年冻记,在試婚紗的時候發(fā)現(xiàn)自己被綠了睡毒。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,919評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡冗栗,死狀恐怖演顾,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情隅居,我是刑警寧澤钠至,帶...
    沈念sama閱讀 35,635評論 5 345
  • 正文 年R本政府宣布,位于F島的核電站胎源,受9級特大地震影響棉钧,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜涕蚤,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,237評論 3 329
  • 文/蒙蒙 一宪卿、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧万栅,春花似錦佑钾、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,855評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春邮偎,著一層夾襖步出監(jiān)牢的瞬間管跺,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,983評論 1 269
  • 我被黑心中介騙來泰國打工禾进, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留豁跑,地道東北人。 一個月前我還...
    沈念sama閱讀 48,048評論 3 370
  • 正文 我出身青樓泻云,卻偏偏與公主長得像艇拍,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子宠纯,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,864評論 2 354

推薦閱讀更多精彩內(nèi)容