Java多線程(6)-- 分工之線程池

? ? ? ?使用線程的時候就去創(chuàng)建一個線程,這樣實現(xiàn)起來非常簡便,但是就會有一個問題:

  如果并發(fā)的線程數(shù)量很多,并且每個線程都是執(zhí)行一個時間很短的任務就結(jié)束了迹淌,這樣頻繁創(chuàng)建線程就會大大降低系統(tǒng)的效率,因為頻繁創(chuàng)建線程和銷毀線程需要時間己单。

  那么有沒有一種辦法使得線程可以復用唉窃,就是執(zhí)行完一個任務,并不被銷毀纹笼,而是可以繼續(xù)執(zhí)行其他的任務纹份?在Java中可以通過線程池來達到這樣的效果。

合理利用線程池能夠帶來三個好處。第一:降低資源消耗蔓涧。通過重復利用已創(chuàng)建的線程降低線程創(chuàng)建和銷毀造成的消耗件已。第二:提高響應速度。當任務到達時元暴,任務可以不需要的等到線程創(chuàng)建就能立即執(zhí)行篷扩。第三:提高線程的可管理性。線程是稀缺資源茉盏,如果無限制的創(chuàng)建鉴未,不僅會消耗系統(tǒng)資源,還會降低系統(tǒng)的穩(wěn)定性鸠姨,使用線程池可以進行統(tǒng)一的分配铜秆,調(diào)優(yōu)和監(jiān)控。

線程池繼承關系圖:

ThreadPoolExecutor享怀、AbstractExecutorService羽峰、ExecutorService和Executor幾個之間的關系:

  Executor是一個頂層接口趟咆,在它里面只聲明了一個方法execute(Runnable)添瓷,返回值為void,參數(shù)為Runnable類型值纱,從字面意思可以理解鳞贷,就是用來執(zhí)行傳進去的任務的;

  然后ExecutorService接口繼承了Executor接口虐唠,并聲明了一些方法:submit搀愧、invokeAll、invokeAny以及shutDown等疆偿,也提供了更加全面的提交任務機制咱筛,如返回Future而不是void的submit方法。注意杆故,這個方法的輸入是Callable迅箩,它解決了Runnable無法返回結(jié)果的困擾;

  抽象類AbstractExecutorService實現(xiàn)了ExecutorService接口处铛,基本實現(xiàn)了ExecutorService中聲明的所有方法饲趋;

  然后ThreadPoolExecutor繼承了類AbstractExecutorService。

? ? ? ?Java標準庫提供了幾種基礎實現(xiàn)撤蟆,比如ThreadPoolExecutor奕塑、ScheduledThreadPoolExecutor、ForkJoinPool家肯。這些線程池的設計特點在于其高度的可調(diào)節(jié)性和靈活性龄砰,以盡量滿足復雜多變的實際應用場景。

? ? ? ? Executors則從簡化使用的角度讨衣,為我們提供了各種方便的靜態(tài)工廠方法寝贡。


java.uitl.concurrent.ThreadPoolExecutor類是線程池中最核心的一個類扒披,因此如果要透徹地了解Java中的線程池,必須先了解這個類圃泡。

public?ThreadPoolExecutor(int?corePoolSize, int?maximumPoolSize,long?keepAliveTime, TimeUnit unit, BlockingQueueworkQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler);

參數(shù)的含義:

corePoolSize:線程池的基本大小碟案。默認情況下,在創(chuàng)建了線程池后颇蜡,線程池中的線程數(shù)為0价说,當有任務來之后,就會創(chuàng)建一個線程去執(zhí)行任務风秤,當線程池中的線程數(shù)目達到corePoolSize后鳖目,就會把到達的任務放到緩存隊列當中;

maximumPoolSize:線程池中允許的最大線程數(shù)缤弦。如果隊列滿了领迈,并且已創(chuàng)建的線程數(shù)小于最大線程數(shù),則線程池會再創(chuàng)建新的線程執(zhí)行任務碍沐。值得注意的是如果使用了無界的任務隊列這個參數(shù)就沒什么效果狸捅。注意還有一個largestPoolSize,記錄了曾經(jīng)出現(xiàn)的最大線程個數(shù)累提。因為setMaximumPoolSize()可以改變最大線程數(shù)尘喝。

poolSize:線程池中當前線程的數(shù)量。

keepAliveTime:表示線程沒有任務執(zhí)行時最多保持多久時間會終止斋陪。默認情況下朽褪,只有當線程池中的線程數(shù)大于corePoolSize時,keepAliveTime才會起作用无虚,直到線程池中的線程數(shù)不大于corePoolSize缔赠,即當線程池中的線程數(shù)大于corePoolSize時,如果一個線程空閑的時間達到keepAliveTime友题,則會終止嗤堰,直到線程池中的線程數(shù)不超過corePoolSize。但是如果調(diào)用了allowCoreThreadTimeOut(boolean)方法咆爽,在線程池中的線程數(shù)不大于corePoolSize時梁棠,keepAliveTime參數(shù)也會起作用,直到線程池中的線程數(shù)為0斗埂;

workQueue(任務隊列):用于保存等待執(zhí)行的任務的阻塞隊列符糊。可以選擇以下幾個阻塞隊列:

(1)ArrayBlockingQueue:是一個基于數(shù)組結(jié)構(gòu)的有界阻塞隊列呛凶,此隊列按 FIFO(先進先出)原則對元素進行排序男娄。

(2)LinkedBlockingQueue:一個基于鏈表結(jié)構(gòu)的阻塞隊列,此隊列按FIFO (先進先出) 排序元素,吞吐量通常要高于ArrayBlockingQueue模闲。其實也是有界隊列建瘫,但是不設置大小時就時Integer.MAX_VALUE。靜態(tài)工廠方法Executors.newFixedThreadPool()和Executors.newSingleThreadExecutor()使用了這個隊列尸折。

(3)SynchronousQueue:一個不存儲元素的阻塞隊列啰脚。每個插入操作必須等到另一個線程調(diào)用移除操作,否則插入操作一直處于阻塞狀態(tài)实夹,吞吐量通常要高于LinkedBlockingQueue橄浓,靜態(tài)工廠方法Executors.newCachedThreadPool使用了這個隊列。

(4)PriorityBlockingQueue:一個具有優(yōu)先級得無界阻塞隊列亮航。

handler(任務拒絕策略):

當線程池的任務緩存隊列已滿并且線程池中的線程數(shù)目達到maximumPoolSize荸实,如果還有任務到來就會采取任務拒絕策略,通常有以下四種策略:

ThreadPoolExecutor.AbortPolicy:丟棄任務并拋出RejectedExecutionException異常缴淋。

ThreadPoolExecutor.DiscardPolicy:也是丟棄任務准给,但是不拋出異常。

ThreadPoolExecutor.DiscardOldestPolicy:丟棄隊列最前面的任務重抖,然后重新嘗試執(zhí)行任務(重復此過程)

ThreadPoolExecutor.CallerRunsPolicy:由調(diào)用線程處理該任務


那么poolSize露氮、corePoolSizemaximumPoolSize三者的關系是如何的呢仇哆?

有界隊列:

當新提交一個任務時:

(1)如果poolSize < corePoolSize沦辙,新增加一個線程處理新的任務夫植。

(2)如果poolSize = corePoolSize讹剔,新任務會被放入阻塞隊列等待。

(3)如果阻塞隊列的容量達到上限详民,且這時poolSize < maximumPoolSize延欠,新增線程來處理任務。

(4)如果阻塞隊列滿了沈跨,且poolSize = maximumPoolSize由捎,那么線程池已經(jīng)達到極限,會根據(jù)飽和策略RejectedExecutionHandler拒絕新的任務饿凛。

(5)如果線程池中的線程數(shù)量大于 corePoolSize時狞玛,且某線程空閑時間超過keepAliveTime,線程將被終止涧窒,直至線程池中的線程數(shù)目不大于corePoolSize心肪;如果允許為核心池中的線程設置存活時間,那么核心池中的線程空閑時間超過keepAliveTime纠吴,線程也會被終止硬鞍。

無界隊列

與有界隊列相比,除非系統(tǒng)資源耗盡,否則無界的任務隊列不存在任務入隊失敗的情況固该。當有新的任務到來锅减,系統(tǒng)的線程數(shù)小于corePoolSize時,則新建線程執(zhí)行任務伐坏。當達到corePoolSize后怔匣,就不會繼續(xù)增加,若后續(xù)仍有新的任務加入桦沉,而沒有空閑的線程資源劫狠,則任務直接進入隊列等待。若任務創(chuàng)建和處理的速度差異很大永部,無界隊列會保持快速增長独泞,直到耗盡系統(tǒng)內(nèi)存。

https://blog.csdn.net/kusedexingfu/article/details/72491864


幾個工廠方法:

不過在java doc中苔埋,并不提倡我們直接使用ThreadPoolExecutor懦砂,而是使用Executors類中提供的幾個靜態(tài)方法來創(chuàng)建線程池:

Executors.newCachedThreadPool();??????? //創(chuàng)建一個線程池,容量大小為??? ?????

????????????????????????????????????????????????????????????????????Integer.MAX_VALUE

Executors.newSingleThreadExecutor();?? //創(chuàng)建容量為1的線程池

Executors.newFixedThreadPool(intnThreads);??? //創(chuàng)建固定容量大小的線程池

Executors.newSingleThreadScheduledExecutor()和newScheduledThreadPool(int corePoolSize)??//創(chuàng)建的是一個ScheduledExecutorService组橄,可以進行定時或周期性的工作調(diào)度荞膘,區(qū)別在于單一工作線程還是多個工作線程。

Executors.newWorkStealingPool(intparallelism)????//這是一個經(jīng)常被人忽略的線程池玉工,Java8才加入這個創(chuàng)建方法羽资,其內(nèi)部會構(gòu)建ForkJoinPool,利用Work-Stealing算法遵班,并行的處理任務屠升,不保證處理順序。

實現(xiàn)細節(jié):

public static ExecutorService newFixedThreadPool(int nThreads) {

??? return newThreadPoolExecutor(nThreads, nThreads,

?????????????????????????????????0L, TimeUnit.MILLISECONDS,

?????????????????????????????????new LinkedBlockingQueue());

}

public static ExecutorService newSingleThreadExecutor() {

??? return newFinalizableDelegatedExecutorService

??????? (newThreadPoolExecutor(1, 1,

???????????????????????????????0L, TimeUnit.MILLISECONDS,

???????????????????????????????new LinkedBlockingQueue()));

}

public static ExecutorService newCachedThreadPool() {

??? return newThreadPoolExecutor(0, Integer.MAX_VALUE,

?????????????????????????????????60L, TimeUnit.SECONDS,

?????????????????????????????????new SynchronousQueue());

}


從它們的具體實現(xiàn)來看狭郑,它們實際上也是調(diào)用了ThreadPoolExecutor腹暖,只不過參數(shù)都已配置好了。

newFixedThreadPool創(chuàng)建的線程池corePoolSize和maximumPoolSize值是相等的翰萨,它使用的LinkedBlockingQueue脏答;

newSingleThreadExecutor將corePoolSize和maximumPoolSize都設置為1,也使用的LinkedBlockingQueue亩鬼;

newCachedThreadPool將corePoolSize設置為0殖告,將maximumPoolSize設置為Integer.MAX_VALUE,使用的SynchronousQueue雳锋,也就是說來了任務就創(chuàng)建線程運行黄绩,當線程空閑超過60秒,就銷毀線程魄缚。


向線程池提交任務:

我們可以使用execute提交的任務宝与,但是execute方法沒有返回值焚廊,所以無法判斷任務知否被線程池執(zhí)行成功。通過以下代碼可知execute方法輸入的任務是一個Runnable類的實例习劫。

threadsPool.execute(new Runnable() {

??? @Override

??? public void run() {

??? // TODO Auto-generatedmethod stub

? ? }

});

我們也可以使用submit 方法來提交任務咆瘟,它會返回一個future,那么我們可以通過這個future來判斷任務是否執(zhí)行成功,通過future的get方法來獲取返回值诽里,

get方法會阻塞住直到任務完成袒餐,而使用get(long timeout, TimeUnit unit)方法則會阻塞一段時間后立即返回,這時有可能任務沒有執(zhí)行完谤狡。

try {

??? Object s = future.get();

??? } catch(InterruptedException e) {

??? //處理中斷異常

??? } catch(ExecutionException e) {

??? //處理無法執(zhí)行任務異常

??? } finally {

??? //關閉線程池

??? executor.shutdown();

}


線程池的關閉:

ThreadPoolExecutor提供了兩個方法灸眼,用于線程池的關閉,分別是shutdown()和shutdownNow()墓懂,其中:

shutdown():不會立即終止線程池焰宣,而是要等所有任務緩存隊列中的任務都執(zhí)行完后才終止,但再也不會接受新的任務捕仔。

shutdownNow():立即終止線程池匕积,并嘗試打斷正在執(zhí)行的任務,并且清空任務緩存隊列榜跌,返回尚未執(zhí)行的任務闪唆。


線程池的更多細節(jié)可參考:https://www.cnblogs.com/dolphin0520/p/3932921.html

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市钓葫,隨后出現(xiàn)的幾起案子悄蕾,更是在濱河造成了極大的恐慌,老刑警劉巖础浮,帶你破解...
    沈念sama閱讀 217,826評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件帆调,死亡現(xiàn)場離奇詭異,居然都是意外死亡霸旗,警方通過查閱死者的電腦和手機贷帮,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,968評論 3 395
  • 文/潘曉璐 我一進店門戚揭,熙熙樓的掌柜王于貴愁眉苦臉地迎上來诱告,“玉大人,你說我怎么就攤上這事民晒【樱” “怎么了?”我有些...
    開封第一講書人閱讀 164,234評論 0 354
  • 文/不壞的土叔 我叫張陵潜必,是天一觀的道長靴姿。 經(jīng)常有香客問我,道長磁滚,這世上最難降的妖魔是什么佛吓? 我笑而不...
    開封第一講書人閱讀 58,562評論 1 293
  • 正文 為了忘掉前任宵晚,我火速辦了婚禮,結(jié)果婚禮上维雇,老公的妹妹穿的比我還像新娘淤刃。我一直安慰自己,他們只是感情好吱型,可當我...
    茶點故事閱讀 67,611評論 6 392
  • 文/花漫 我一把揭開白布逸贾。 她就那樣靜靜地躺著,像睡著了一般津滞。 火紅的嫁衣襯著肌膚如雪铝侵。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,482評論 1 302
  • 那天触徐,我揣著相機與錄音咪鲜,去河邊找鬼。 笑死撞鹉,一個胖子當著我的面吹牛嗜诀,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播孔祸,決...
    沈念sama閱讀 40,271評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼隆敢,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了崔慧?” 一聲冷哼從身側(cè)響起拂蝎,我...
    開封第一講書人閱讀 39,166評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎惶室,沒想到半個月后温自,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,608評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡皇钞,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,814評論 3 336
  • 正文 我和宋清朗相戀三年悼泌,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片夹界。...
    茶點故事閱讀 39,926評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡馆里,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出可柿,到底是詐尸還是另有隱情鸠踪,我是刑警寧澤,帶...
    沈念sama閱讀 35,644評論 5 346
  • 正文 年R本政府宣布复斥,位于F島的核電站营密,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏目锭。R本人自食惡果不足惜评汰,卻給世界環(huán)境...
    茶點故事閱讀 41,249評論 3 329
  • 文/蒙蒙 一纷捞、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧被去,春花似錦兰绣、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,866評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至踪央,卻和暖如春臀玄,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背畅蹂。 一陣腳步聲響...
    開封第一講書人閱讀 32,991評論 1 269
  • 我被黑心中介騙來泰國打工健无, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人液斜。 一個月前我還...
    沈念sama閱讀 48,063評論 3 370
  • 正文 我出身青樓累贤,卻偏偏與公主長得像,于是被迫代替她去往敵國和親少漆。 傳聞我的和親對象是個殘疾皇子臼膏,可洞房花燭夜當晚...
    茶點故事閱讀 44,871評論 2 354