快速了解Java線程池

本文同時載于本人CSDN

1.概述

使用Thread類執(zhí)行某個任務床估,任務運行時每次都要創(chuàng)建線程雇寇,任務結束則要銷毀線程,對系統(tǒng)而言虽另,線程不僅是資源暂刘,線程的創(chuàng)建與銷毀也要消耗系統(tǒng)的資源。對于此問題捂刺,很顯然一個直接的解決方案就是復用這個線程谣拣,讓線程執(zhí)行完某個任務后仍然能繼續(xù)執(zhí)行執(zhí)行其它任務,而不是銷毀族展。線程池就提供了這樣的解決方案芝发。

線程池指的是存在一組線程,這組線程創(chuàng)建后一般不會銷毀苛谷,而是進入休眠狀態(tài)辅鲸,等到有新的任務需要執(zhí)行又重新恢執(zhí)行。

2.創(chuàng)建一個線程池

java.util.concurrent(JUC)包提供了很多接口用于線程池的創(chuàng)建腹殿。

2.1 ExecutorService 接口

線程池通常指的是ExecutorService —— 一個提供了終止管理的方法和可以生成Future的方法的Executor独悴。簡單地說例书,ExecutorService接口繼承了Executor接口,ExecutorService除了存在Executor提供的execute方法外刻炒,還提供了很多終止管理的方法以及產生用于跟蹤線程執(zhí)行結果的Future的submit决采。如下所示


boolean awaitTermination(long timeout, TimeUnit unit)

<T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks)

<T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit)

<T> T   invokeAny(Collection<? extends Callable<T>> tasks)

<T> T   invokeAny(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit)

boolean isShutdown()

boolean isTerminated()

void    shutdown()

List<Runnable>  shutdownNow()

<T> Future<T>   submit(Callable<T> task)

Future<?>   submit(Runnable task)

<T> Future<T>   submit(Runnable task, T result)

2.2 直接通過 ThreadPoolExecutor創(chuàng)建線程池

JUC提供了ThreadPoolExecutor類用于創(chuàng)建線程池,這個類繼承了實現ExecutorService接口的AbstractThreadPoolExecutor坟奥。ThreadPoolExecutor有若干個構造參數树瞭,一般可以通過調用如下所示的創(chuàng)建一個線程池:

ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue)

代碼如下

ExecutorService executor = new TreadPoolExecutor(5, 10, 10L,TimeUnit.SECONDS,new BlockingArrayQueue(40));

其中,各個參數的含義如下:

  • corePoolSize 線程池中保持存活的線程數量爱谁,即使線程是空閑的晒喷,除非調用allowCoreThreadTimeOut(true)
  • corePoolSize 線程池中最大能創(chuàng)建的線程數量
  • keepAliveTime 空閑線程的存活時間
  • unit KeepAliveTime參數的單位例如 TimeUnit.SECONDES等
  • workQueue 一個儲存所提交的runnable的任務的隊列,只會execute方法提交的runnable任務

通過上面的參數描述访敌,我們可以知道凉敲,通過ThreadPoolExecutor創(chuàng)建的的線程池是可以自動調整大小的。當一個新的任務提交時寺旺,如果此時線程池中的線程小于corePoolSize爷抓,那么線程池就會創(chuàng)建一個新的線程來執(zhí)行此任務,即使其它的線程處于空閑狀態(tài)阻塑。如果此時線程池中存在大于corePoolSize但是小于maximumPoolSize的線程數蓝撇,除非workQueue是滿的,否則不會創(chuàng)建新的線程陈莽。因此唉地,我們可以通過設置corePoolSizemaximumPoolSize相等來創(chuàng)建一個固定線程數的線程池,也可以通過設置一個實質上沒有邊界的的值例如Integer.MAX_VALUE來創(chuàng)建可以容納任意并發(fā)線程數的線程池传透。一般來說,corePoolSizemaximumPoolSize在線程池構造時就已經確定极颓,但是我們也可以通過 setCorePoolSize(int)setMaximumPoolSize(int)動態(tài)設置這兩個值朱盐。

2.3 線程池中線程的創(chuàng)建

線程池使用ThreadFactory創(chuàng)建新線程。如果沒有另外指定菠隆,則使用Executors.defaultThreadFactory()兵琳,它將所有線程創(chuàng)建在同一個ThreadGroup中,并具有相同的NORM_PRIORITY優(yōu)先級和非守護進程狀態(tài)骇径。 通過提供不同的ThreadFactory躯肌,我們可以更改線程的名稱,線程組破衔,優(yōu)先級清女,守護程序狀態(tài)等。如果ThreadFactory調用newThread時返回null創(chuàng)建線程失敗晰筛,則executor將繼續(xù)嫡丙,但可能無法執(zhí)行任何任務拴袭。 線程應該擁有“modifyThread”的RuntimePermission。如果使用線程池的工作線程或其他線程不具有此權限曙博,則服務可能會降級:配置更改可能不會及時生效拥刻,并且關閉線程池可能保持可以終止但未完成的狀態(tài)。

2.4 線程存活時間

如果池當前具有多于corePoolSize的線程父泳,則多余線程如果空閑時間超過keepAliveTime般哼,則將終止(請參閱getKeepAliveTime(TimeUnit))。 這提供了一種在不使用池時減少資源消耗的方法惠窄。如果線程池稍后變得更活躍蒸眠,則將構造新線程。 也可以使用方法setKeepAliveTime(long睬捶,TimeUnit)動態(tài)更改此參數黔宛。使用Long.MAX_VALUE TimeUnit.NANOSECONDS這樣的值將有效地禁止空閑線程在線程池關閉之前終止。 默認情況下擒贸,僅當存在大于corePoolSize的線程時臀晃,保持活動策略才適用。但是方法allowCoreThreadTimeOut(boolean)也可用于將此超時策略應用于核心線程介劫,只要keepAliveTime值不為零徽惋。

2.5 工作隊列

任何BlockingQueue都可用于傳輸和保存提交的任務。此隊列的使用與池大小相互影響:

  • 如果運行的線程少于corePoolSize座韵,則Executor總是更喜歡添加新線程而不是將任務加入隊列险绘。
  • 如果corePoolSize或更多線程正在運行,則Executor總是更喜歡排隊請求而不是添加新線程誉碴。
  • 如果隊列已經滿了宦棺,則會創(chuàng)建一個新線程,除非它超過maximumPoolSize黔帕,在這種情況下代咸,該任務將被拒絕。

隊列選擇有三種常規(guī)策略:

  • 直接交接成黄。工作隊列的一個很好的默認選擇是SynchronousQueue呐芥,它將任務交給線程而不另外保存它們。如果沒有線程立即可用于執(zhí)行任務奋岁,則嘗試添加一個新的任務將會失敗思瘟,因此將構造新線程。此策略在處理可能具有內部依賴性的請求集時避免了鎖定闻伶。直接交接法通常需要無邊界的maximumPoolSizes以避免拒絕新提交的任務滨攻。這也就意味著,當任務以比處理它們更快的速度到達時,線程可能會無限制增長铡买。
  • 無限隊列更鲁。使用無限制隊列(例如,沒有預定義容量的LinkedBlockingQueue)將導致新任務在沒有空閑線程時在隊列中等待奇钞。因此澡为,線程池只會創(chuàng)建corePoolSize數量的線程(maximumPoolSize的值因此沒有任何效果)。當每個任務完全獨立于其他任務時景埃,這可能是適當的媒至,因此任務不會影響彼此的執(zhí)行;例如,在網頁服務器中谷徙。雖然這種排隊方式可以有助于平滑瞬態(tài)突發(fā)請求拒啰,但是這同時意味著當任務繼續(xù)平均到達的速度超過可處理速度時,工作隊列可能會無限制增長完慧。
  • 有限的隊列谋旦。有限隊列(例如,ArrayBlockingQueue)與有限maximumPoolSizes一起使用時有助于防止資源耗盡屈尼,但可能更難以調整和控制册着。隊列大小和最大池大小之間可以相互折中(trade off):使用大型隊列和小型池可以最小化CPU使用率,OS資源和上下文切換開銷脾歧,但可能導致人為的低吞吐量甲捏。如果任務經常阻塞(例如,如果它們執(zhí)行時存在I/O操作)鞭执,則系統(tǒng)可能能夠提供比maximumPoolSizes更多的線程調度時間司顿。使用小隊列通常需要更大的池大小,這會使CPU更加繁忙兄纺,但可能會遇到不可接受的調度開銷大溜,這也會降低吞吐量。

2.6 任務提交

提交任務有兩種方法估脆,一種是提交無返回值的任務钦奋,使用如下方式:

ExecutorService executor = new TreadPoolExecutor(5, 10, 10L,TimeUnit.SECONDS,new BlockingArrayQueue(40));
Runnable handler = ......;
executor.execute(handler);

如果需要獲得任務的執(zhí)行結果,則可以通過調用submit方法來提交任務旁蔼,代碼如下:

ExecutorService executor = new TreadPoolExecutor(5, 10, 10L,TimeUnit.SECONDS,new BlockingArrayQueue(40));
Callable handler = ......;
Future future = executor.submit(handler);

2.7 創(chuàng)建線程池的完整實例

import java.util.concurrent.*;

public TreadPoolTest(){
    public static void main(String[] args){
        ExecutorService executor = new TreadPoolExecutor(5, 10, 10L,TimeUnit.SECONDS,new ArrayBlockingQueue(40));
        for(int i = 0;i < 10; i++){
            Handler handler = new Handler();
            executor.execute(handler);
        }
        executor.shutdown();

    }

    class Handler extends Runnable{
        public void run(){
            for(int i = 0; i < 5;i++){
                System.out.println(i);
            }
        }
    }
}

3 通過Executor的工廠方法創(chuàng)建線程池

Executors是JUC包下的一個工廠類,他提供了許多便捷的方式創(chuàng)建線程池疙教。

3.1 創(chuàng)建固定大小的線程池

可以通過Executors類創(chuàng)建一個固定大小的線程池:

ExecutorService executor = Executors.newFixedThreadPool(10);

我們前面已經說過棺聊,固定大小的線程池可以通過設置ThreadPoolExecutor 核心線程數和最大線程數相等實現,事實上源碼也是如此的:

public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
        0L, TimeUnit.MILLISECONDS,
        new LinkedBlockingQueue<Runnable>());
    }

一般來說贞谓,當線程數達到核心線程數限佩,既不會再創(chuàng)建線程,也不會銷毀線程,除非發(fā)生異常某個線程終止,或者設置allowCoreTimeOut祟同,但最好別這么做作喘,因為keepAlive參數此時被設置為0,可能會導致頻繁的創(chuàng)建與銷毀線程(這里僅僅是推測。仍需驗證)晕城。

3.2 創(chuàng)建可緩存的線程池:

可以通過Executors.newCachedThreadPool 創(chuàng)建一個可緩存的線程池泞坦,如果當前線程池的規(guī)模超出了處理需求,將回收空的線程砖顷;當需求增加時贰锁,會增加線程數量;線程池規(guī)模無限制滤蝠。代碼如下:

ExecutorService executor = Executors.newCachedThreadPool(10);

前面同樣也提到豌熄,可以設置maxmumPoolSize參數為一個實質上無邊界的值如Integer.MAX_VALUE讓線程池的規(guī)模無限制,實際上源碼也是如此實現的:

public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
        60L, TimeUnit.SECONDS,
        SynchronousQueue<Runnable>());
    }

3.3 創(chuàng)建單線程的線程池

可以通過Executors.newSingleThreadExecutor() 創(chuàng)建一個單線程的Executor物咳,確保任務串行執(zhí)行:

ExecutorService executor = Executors.newSingleExecutor();
public static ExecutorService newSingleThreadExecutor() {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
            0L, TimeUnit.MILLISECONDS,
            new LinkedBlockingQueue<Runnable>()));
    }

4 總結

這篇文章深入描述線程池的創(chuàng)建方法者蠕,各個參數的意義與作用儿普,但是線程池除以上所描述的,還存在可調度的線程池,在本系列的下一篇文章將講述這一類線程池逞壁。

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市萌壳,隨后出現的幾起案子场梆,更是在濱河造成了極大的恐慌,老刑警劉巖晴弃,帶你破解...
    沈念sama閱讀 216,324評論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件掩幢,死亡現場離奇詭異,居然都是意外死亡上鞠,警方通過查閱死者的電腦和手機际邻,發(fā)現死者居然都...
    沈念sama閱讀 92,356評論 3 392
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來芍阎,“玉大人世曾,你說我怎么就攤上這事∏聪蹋” “怎么了轮听?”我有些...
    開封第一講書人閱讀 162,328評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長岭佳。 經常有香客問我血巍,道長,這世上最難降的妖魔是什么珊随? 我笑而不...
    開封第一講書人閱讀 58,147評論 1 292
  • 正文 為了忘掉前任述寡,我火速辦了婚禮柿隙,結果婚禮上,老公的妹妹穿的比我還像新娘鲫凶。我一直安慰自己禀崖,他們只是感情好,可當我...
    茶點故事閱讀 67,160評論 6 388
  • 文/花漫 我一把揭開白布螟炫。 她就那樣靜靜地躺著波附,像睡著了一般。 火紅的嫁衣襯著肌膚如雪不恭。 梳的紋絲不亂的頭發(fā)上叶雹,一...
    開封第一講書人閱讀 51,115評論 1 296
  • 那天,我揣著相機與錄音换吧,去河邊找鬼折晦。 笑死,一個胖子當著我的面吹牛沾瓦,可吹牛的內容都是我干的满着。 我是一名探鬼主播,決...
    沈念sama閱讀 40,025評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼贯莺,長吁一口氣:“原來是場噩夢啊……” “哼风喇!你這毒婦竟也來了?” 一聲冷哼從身側響起缕探,我...
    開封第一講書人閱讀 38,867評論 0 274
  • 序言:老撾萬榮一對情侶失蹤魂莫,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后爹耗,有當地人在樹林里發(fā)現了一具尸體耙考,經...
    沈念sama閱讀 45,307評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,528評論 2 332
  • 正文 我和宋清朗相戀三年潭兽,在試婚紗的時候發(fā)現自己被綠了倦始。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,688評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡山卦,死狀恐怖鞋邑,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情账蓉,我是刑警寧澤枚碗,帶...
    沈念sama閱讀 35,409評論 5 343
  • 正文 年R本政府宣布,位于F島的核電站铸本,受9級特大地震影響肮雨,放射性物質發(fā)生泄漏。R本人自食惡果不足惜归敬,卻給世界環(huán)境...
    茶點故事閱讀 41,001評論 3 325
  • 文/蒙蒙 一酷含、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧汪茧,春花似錦椅亚、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,657評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至扩灯,卻和暖如春媚赖,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背珠插。 一陣腳步聲響...
    開封第一講書人閱讀 32,811評論 1 268
  • 我被黑心中介騙來泰國打工惧磺, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人捻撑。 一個月前我還...
    沈念sama閱讀 47,685評論 2 368
  • 正文 我出身青樓磨隘,卻偏偏與公主長得像,于是被迫代替她去往敵國和親顾患。 傳聞我的和親對象是個殘疾皇子番捂,可洞房花燭夜當晚...
    茶點故事閱讀 44,573評論 2 353

推薦閱讀更多精彩內容