Java線程池源碼

使用 Executors 創(chuàng)建線程池


1.newFixedThreadPool()


由于使用了LinkedBlockingQueue所以maximumPoolSize沒用恨胚,當(dāng)corePoolSize滿了之后就加入到LinkedBlockingQueue隊(duì)列中瘤缩。
每當(dāng)某個線程執(zhí)行完成之后就從LinkedBlockingQueue隊(duì)列中取一個。
所以這個是創(chuàng)建固定大小的線程池衫冻。

源碼分析:

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

2.newSingleThreadPool()


創(chuàng)建線程數(shù)為1的線程池钮追,由于使用了LinkedBlockingQueue所以maximumPoolSize 沒用晃财,corePoolSize為1表示線程數(shù)大小為1,滿了就放入隊(duì)列中,執(zhí)行完了就從隊(duì)列取一個建炫。

源碼分析

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

public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue) {
    this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
            Executors.defaultThreadFactory(), defaultHandler);
}

3.newCachedThreadPool()


創(chuàng)建可緩沖的線程池畦韭。沒有大小限制。由于corePoolSize為0所以任務(wù)會放入SynchronousQueue隊(duì)列中肛跌,SynchronousQueue只能存放大小為1艺配,所以會立刻新起線程察郁,由于maxumumPoolSize為Integer.MAX_VALUE所以可以認(rèn)為大小為2147483647。受內(nèi)存大小限制妒挎。

源碼分析

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

public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue) {
    this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
            Executors.defaultThreadFactory(), defaultHandler);
}

使用 ThreadPoolExecutor 創(chuàng)建線程池


源碼分析 ,ThreadPoolExecutor 的構(gòu)造函數(shù)

public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue,
                          ThreadFactory threadFactory,
                          RejectedExecutionHandler handler) {
    if (corePoolSize < 0 ||
            maximumPoolSize <= 0 ||
            maximumPoolSize < corePoolSize ||
            keepAliveTime < 0)
        throw new IllegalArgumentException();
    if (workQueue == null || threadFactory == null || handler == null)
        throw new NullPointerException();
    this.corePoolSize = corePoolSize;
    this.maximumPoolSize = maximumPoolSize;
    this.workQueue = workQueue;
    this.keepAliveTime = unit.toNanos(keepAliveTime);
    this.threadFactory = threadFactory;
    this.handler = handler;
}

構(gòu)造函數(shù)參數(shù)


1绳锅、corePoolSize 核心線程數(shù)大小,當(dāng)線程數(shù) < corePoolSize 酝掩,會創(chuàng)建線程執(zhí)行 runnable

2鳞芙、maximumPoolSize 最大線程數(shù), 當(dāng)線程數(shù) >= corePoolSize的時候期虾,會把 runnable 放入 workQueue中

3原朝、keepAliveTime 保持存活時間,當(dāng)線程數(shù)大于corePoolSize的空閑線程能保持的最大時間镶苞。

4喳坠、unit 時間單位

5、workQueue 保存任務(wù)的阻塞隊(duì)列

6茂蚓、threadFactory 創(chuàng)建線程的工廠

7壕鹉、handler 拒絕策略

任務(wù)執(zhí)行順序


1、當(dāng)線程數(shù)小于 corePoolSize時聋涨,創(chuàng)建線程執(zhí)行任務(wù)晾浴。

2、當(dāng)線程數(shù)大于等于 corePoolSize并且 workQueue 沒有滿時牍白,放入workQueue中

3脊凰、線程數(shù)大于等于 corePoolSize并且當(dāng) workQueue 滿時,新任務(wù)新建線程運(yùn)行茂腥,線程總數(shù)要小于 maximumPoolSize

4狸涌、當(dāng)線程總數(shù)等于 maximumPoolSize 并且 workQueue 滿了的時候執(zhí)行 handler 的 rejectedExecution。也就是拒絕策略最岗。

JDK7提供了7個阻塞隊(duì)列帕胆。(也屬于并發(fā)容器)

1、 ArrayBlockingQueue :一個由數(shù)組結(jié)構(gòu)組成的有界阻塞隊(duì)列仑性。
2惶楼、LinkedBlockingQueue :一個由鏈表結(jié)構(gòu)組成的有界阻塞隊(duì)列。
3诊杆、PriorityBlockingQueue :一個支持優(yōu)先級排序的無界阻塞隊(duì)列歼捐。
4、DelayQueue:一個使用優(yōu)先級隊(duì)列實(shí)現(xiàn)的無界阻塞隊(duì)列晨汹。
5豹储、SynchronousQueue:一個不存儲元素的阻塞隊(duì)列。
6淘这、LinkedTransferQueue:一個由鏈表結(jié)構(gòu)組成的無界阻塞隊(duì)列剥扣。
7巩剖、LinkedBlockingDeque:一個由鏈表結(jié)構(gòu)組成的雙向阻塞隊(duì)列。

什么是阻塞隊(duì)列钠怯?


阻塞隊(duì)列是一個在隊(duì)列基礎(chǔ)上又支持了兩個附加操作的隊(duì)列佳魔。

2個附加操作:

支持阻塞的插入方法:隊(duì)列滿時,隊(duì)列會阻塞插入元素的線程晦炊,直到隊(duì)列不滿鞠鲜。
支持阻塞的移除方法:隊(duì)列空時,獲取元素的線程會等待隊(duì)列變?yōu)榉强铡?/p>

阻塞隊(duì)列的應(yīng)用場景

阻塞隊(duì)列常用于生產(chǎn)者和消費(fèi)者的場景断国,生產(chǎn)者是向隊(duì)列里添加元素的線程贤姆,消費(fèi)者是從隊(duì)列里取元素的線程。簡而言之稳衬,阻塞隊(duì)列是生產(chǎn)者用來存放元素霞捡、消費(fèi)者獲取元素的容器。

幾個方法

在阻塞隊(duì)列不可用的時候薄疚,上述2個附加操作提供了四種處理方法


圖片.png

JAVA里的阻塞隊(duì)列

JDK 7 提供了7個阻塞隊(duì)列碧信,如下

1、ArrayBlockingQueue 數(shù)組結(jié)構(gòu)組成的有界阻塞隊(duì)列街夭。

此隊(duì)列按照先進(jìn)先出(FIFO)的原則對元素進(jìn)行排序音婶,但是默認(rèn)情況下不保證線程公平的訪問隊(duì)列,即如果隊(duì)列滿了莱坎,那么被阻塞在外面的線程對隊(duì)列訪問的順序是不能保證線程公平(即先阻塞,先插入)的寸士。

2檐什、LinkedBlockingQueue一個由鏈表結(jié)構(gòu)組成的有界阻塞隊(duì)列

此隊(duì)列按照先出先進(jìn)的原則對元素進(jìn)行排序

3、PriorityBlockingQueue支持優(yōu)先級的無界阻塞隊(duì)列
4弱卡、DelayQueue支持延時獲取元素的無界阻塞隊(duì)列乃正,即可以指定多久才能從隊(duì)列中獲取當(dāng)前元素
5、SynchronousQueue不存儲元素的阻塞隊(duì)列婶博,每一個put必須等待一個take操作瓮具,否則不能繼續(xù)添加元素。并且他支持公平訪問隊(duì)列凡人。
6名党、LinkedTransferQueue由鏈表結(jié)構(gòu)組成的無界阻塞TransferQueue隊(duì)列。相對于其他阻塞隊(duì)列挠轴,多了tryTransfer和transfer方法
transfer方法

如果當(dāng)前有消費(fèi)者正在等待接收元素(take或者待時間限制的poll方法)传睹,transfer可以把生產(chǎn)者傳入的元素立刻傳給消費(fèi)者。如果沒有消費(fèi)者等待接收元素岸晦,則將元素放在隊(duì)列的tail節(jié)點(diǎn)欧啤,并等到該元素被消費(fèi)者消費(fèi)了才返回睛藻。

tryTransfer方法

用來試探生產(chǎn)者傳入的元素能否直接傳給消費(fèi)者。邢隧,如果沒有消費(fèi)者在等待店印,則返回false。和上述方法的區(qū)別是該方法無論消費(fèi)者是否接收倒慧,方法立即返回按摘。而transfer方法是必須等到消費(fèi)者消費(fèi)了才返回。

7迫靖、LinkedBlockingDeque鏈表結(jié)構(gòu)的雙向阻塞隊(duì)列院峡,優(yōu)勢在于多線程入隊(duì)時,減少一半的競爭系宜。

四個拒絕策略

ThreadPoolExecutor默認(rèn)有四個拒絕策略:

1照激、ThreadPoolExecutor.AbortPolicy() 直接拋出異常RejectedExecutionException
2、ThreadPoolExecutor.CallerRunsPolicy() 直接調(diào)用run方法并且阻塞執(zhí)行
3盹牧、ThreadPoolExecutor.DiscardPolicy() 直接丟棄后來的任務(wù)
4俩垃、ThreadPoolExecutor.DiscardOldestPolicy() 丟棄在隊(duì)列中隊(duì)首的任務(wù)

當(dāng)然可以自己繼承RejectedExecutionHandler來寫拒絕策略.
TestThreadPoolExecutor 示例

TestThreadPoolExecutor.java

package io.ymq.thread.TestThreadPoolExecutor;

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * 描述:
 *
 * @author yanpenglei
 * @create 2017-10-12 15:39
 **/
public class TestThreadPoolExecutor {
    public static void main(String[] args) {

        long currentTimeMillis = System.currentTimeMillis();

        // 構(gòu)造一個線程池
        ThreadPoolExecutor threadPool = new ThreadPoolExecutor(5, 6, 3,
                TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(3)
        );

        for (int i = 1; i <= 10; i++) {
            try {
                String task = "task=" + i;
                System.out.println("創(chuàng)建任務(wù)并提交到線程池中:" + task);
                threadPool.execute(new ThreadPoolTask(task));

                Thread.sleep(100);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        try {
            //等待所有線程執(zhí)行完畢當(dāng)前任務(wù)。
            threadPool.shutdown();

            boolean loop = true;
            do {
                //等待所有線程執(zhí)行完畢當(dāng)前任務(wù)結(jié)束
                loop = !threadPool.awaitTermination(2, TimeUnit.SECONDS);//等待2秒
            } while (loop);

            if (loop != true) {
                System.out.println("所有線程執(zhí)行完畢");
            }

        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            System.out.println("耗時:" + (System.currentTimeMillis() - currentTimeMillis));
        }


    }
}

ThreadPoolTask.java

package io.ymq.thread.TestThreadPoolExecutor;

import java.io.Serializable;

/**
 * 描述:
 *
 * @author yanpenglei
 * @create 2017-10-12 15:40
 **/
public class ThreadPoolTask implements Runnable, Serializable {

    private Object attachData;

    ThreadPoolTask(Object tasks) {
        this.attachData = tasks;
    }

    public void run() {

        try {

            System.out.println("開始執(zhí)行任務(wù):" + attachData + "任務(wù)汰寓,使用的線程池口柳,線程名稱:" + Thread.currentThread().getName());

            System.out.println();

        } catch (Exception e) {
            e.printStackTrace();
        }
        attachData = null;
    }

}

遇到j(luò)ava.util.concurrent.RejectedExecutionException

第一

你的線程池 ThreadPoolExecutor 顯示的 shutdown() 之后,再向線程池提交任務(wù)的時候有滑。 如果你配置的拒絕策略是 AbortPolicy 的話跃闹,這個異常就會拋出來。

第二

當(dāng)你設(shè)置的任務(wù)緩存隊(duì)列過小的時候毛好,或者說望艺, 你的線程池里面所有的線程都在干活(線程數(shù)== maxPoolSize),并且你的任務(wù)緩存隊(duì)列也已經(jīng)充滿了等待的隊(duì)列, 這個時候肌访,你再向它提交任務(wù)找默,則會拋出這個異常。

響應(yīng)

可以看到線程 pool-1-thread-1 到5 循環(huán)使用

創(chuàng)建任務(wù)并提交到線程池中:task=1
開始執(zhí)行任務(wù):task=1任務(wù)吼驶,使用的線程池惩激,線程名稱:pool-1-thread-1

創(chuàng)建任務(wù)并提交到線程池中:task=2
開始執(zhí)行任務(wù):task=2任務(wù),使用的線程池蟹演,線程名稱:pool-1-thread-2

創(chuàng)建任務(wù)并提交到線程池中:task=3
開始執(zhí)行任務(wù):task=3任務(wù)风钻,使用的線程池,線程名稱:pool-1-thread-3

創(chuàng)建任務(wù)并提交到線程池中:task=4
開始執(zhí)行任務(wù):task=4任務(wù)酒请,使用的線程池魄咕,線程名稱:pool-1-thread-4

創(chuàng)建任務(wù)并提交到線程池中:task=5
開始執(zhí)行任務(wù):task=5任務(wù),使用的線程池蚌父,線程名稱:pool-1-thread-5

創(chuàng)建任務(wù)并提交到線程池中:task=6
開始執(zhí)行任務(wù):task=6任務(wù)哮兰,使用的線程池毛萌,線程名稱:pool-1-thread-1

創(chuàng)建任務(wù)并提交到線程池中:task=7
開始執(zhí)行任務(wù):task=7任務(wù),使用的線程池喝滞,線程名稱:pool-1-thread-2

創(chuàng)建任務(wù)并提交到線程池中:task=8
開始執(zhí)行任務(wù):task=8任務(wù)阁将,使用的線程池,線程名稱:pool-1-thread-3

創(chuàng)建任務(wù)并提交到線程池中:task=9

轉(zhuǎn)自:https://segmentfault.com/a/1190000011527245

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末右遭,一起剝皮案震驚了整個濱河市做盅,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌窘哈,老刑警劉巖吹榴,帶你破解...
    沈念sama閱讀 221,198評論 6 514
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異滚婉,居然都是意外死亡图筹,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,334評論 3 398
  • 文/潘曉璐 我一進(jìn)店門让腹,熙熙樓的掌柜王于貴愁眉苦臉地迎上來远剩,“玉大人,你說我怎么就攤上這事骇窍」衔睿” “怎么了?”我有些...
    開封第一講書人閱讀 167,643評論 0 360
  • 文/不壞的土叔 我叫張陵腹纳,是天一觀的道長痢掠。 經(jīng)常有香客問我,道長嘲恍,這世上最難降的妖魔是什么志群? 我笑而不...
    開封第一講書人閱讀 59,495評論 1 296
  • 正文 為了忘掉前任,我火速辦了婚禮蛔钙,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘荠医。我一直安慰自己吁脱,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,502評論 6 397
  • 文/花漫 我一把揭開白布彬向。 她就那樣靜靜地躺著兼贡,像睡著了一般。 火紅的嫁衣襯著肌膚如雪娃胆。 梳的紋絲不亂的頭發(fā)上遍希,一...
    開封第一講書人閱讀 52,156評論 1 308
  • 那天,我揣著相機(jī)與錄音里烦,去河邊找鬼凿蒜。 笑死禁谦,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的废封。 我是一名探鬼主播州泊,決...
    沈念sama閱讀 40,743評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼漂洋!你這毒婦竟也來了遥皂?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,659評論 0 276
  • 序言:老撾萬榮一對情侶失蹤刽漂,失蹤者是張志新(化名)和其女友劉穎演训,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體贝咙,經(jīng)...
    沈念sama閱讀 46,200評論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡样悟,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,282評論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了颈畸。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片乌奇。...
    茶點(diǎn)故事閱讀 40,424評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖眯娱,靈堂內(nèi)的尸體忽然破棺而出礁苗,到底是詐尸還是另有隱情,我是刑警寧澤徙缴,帶...
    沈念sama閱讀 36,107評論 5 349
  • 正文 年R本政府宣布试伙,位于F島的核電站,受9級特大地震影響于样,放射性物質(zhì)發(fā)生泄漏疏叨。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,789評論 3 333
  • 文/蒙蒙 一穿剖、第九天 我趴在偏房一處隱蔽的房頂上張望蚤蔓。 院中可真熱鬧,春花似錦糊余、人聲如沸秀又。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,264評論 0 23
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽吐辙。三九已至,卻和暖如春蘸劈,著一層夾襖步出監(jiān)牢的瞬間昏苏,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,390評論 1 271
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留贤惯,地道東北人洼专。 一個月前我還...
    沈念sama閱讀 48,798評論 3 376
  • 正文 我出身青樓,卻偏偏與公主長得像救巷,于是被迫代替她去往敵國和親壶熏。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,435評論 2 359

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

  • 第一部分 來看一下線程池的框架圖,如下: 1精盅、Executor任務(wù)提交接口與Executors工具類 Execut...
    壓抑的內(nèi)心閱讀 4,270評論 1 24
  • 為什么使用線程池 當(dāng)我們在使用線程時帽哑,如果每次需要一個線程時都去創(chuàng)建一個線程,這樣實(shí)現(xiàn)起來很簡單叹俏,但是會有一個問題...
    閩越布衣閱讀 4,293評論 10 45
  • 文章摘要:在業(yè)務(wù)系統(tǒng)中妻枕,線程池框架技術(shù)一直是用來解決多線程并發(fā)的一種有效方法。 在JDK中粘驰,J.U.C并發(fā)包下的T...
    癲狂俠閱讀 2,095評論 2 21
  • 塔羅牌力量屡谐,看著牌我感到這個女人是溫柔而堅(jiān)定的。她輕撫著身邊的獅子蝌数,感覺是在馴服它愕掏。這個女人的頭上的標(biāo)志象征著無限...
    midnightmocca閱讀 116評論 0 0
  • 昨晚闐闐在奶奶家睡得覺,答應(yīng)早上7點(diǎn)回家顶伞,到家8點(diǎn)了饵撑,我之前心里還著急,快8點(diǎn)還不回家唆貌,回來后滑潘,闐爸說了倆句 ...
    麗萍在這閱讀 239評論 2 3