十: 線程池

在實(shí)際生產(chǎn)環(huán)境中,線程的數(shù)量必須得到控制.大量創(chuàng)建線程對(duì)系統(tǒng)性能是有傷害的.
為了避免系統(tǒng)頻繁的創(chuàng)建和銷毀線程,我們可以讓創(chuàng)建的線程進(jìn)行復(fù)用,線程池中,總有那么幾個(gè)活躍線程,當(dāng)需要使用線程時(shí),可以從池子中隨便拿一個(gè)空閑線程,當(dāng)完成工作時(shí),不是馬上關(guān)閉線程,而是將這個(gè)線程放回線程池,方便下次再用.

通過(guò)Executors 可以創(chuàng)建特定功能的線程池,以下是平時(shí)主要用的幾種:

public static ExecutorService newFixedThreadPool(int nThreads)
public static ExecutorService newSingleThreadExecutor()
public static ExecutorService newCachedThreadPool()
  • newFixedThreadPool(int nThreads)方法

    /**
     * @param nThreads the number of threads in the pool
     * @return the newly created thread pool
     * @throws IllegalArgumentException if {@code nThreads <= 0}
     */
    public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
    }

根據(jù)JDK里面注釋可以得到: 該方法返回一個(gè)固定線程數(shù)量的線程池,
當(dāng)有新的任務(wù)提交,線程池中如果有空閑線程,則立即執(zhí)行.如果所有線程都在活動(dòng)狀態(tài),則新的任務(wù)會(huì)被暫存的在一個(gè)任務(wù)隊(duì)列中,等到線程空閑時(shí),就處理任務(wù)隊(duì)列中的任務(wù).

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

改方法傳回一個(gè)只有一個(gè)線程的線程池,多余的任務(wù)提交后將會(huì)保存到任務(wù)隊(duì)列中,待線程空閑,按先入先出的順序執(zhí)行隊(duì)列中的任務(wù).

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

改方法返回一個(gè)可根據(jù)實(shí)際情況調(diào)整線程數(shù)量的線程池.線程池的線程數(shù)量不確定,如果有空閑線程,就用空閑線程,如果所有線程都在工作,又有新的任務(wù)提交,則會(huì)創(chuàng)建新的線程處理任務(wù).

簡(jiǎn)單介紹線程池的用法

public class ThreadPoolDemo {
    public static class Mytask implements Runnable{
        @Override
        public void run() {
            System.out.println(System.currentTimeMillis()+ "---" + Thread.currentThread().getId());

            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] args) {
        Mytask mytask = new Mytask();
        ExecutorService service = Executors.newFixedThreadPool(5);
        for (int i = 0; i < 10; i++) {
            service.submit(mytask);
        }

    }
}

得到運(yùn)行結(jié)果


線程池.png

根據(jù)結(jié)果可以看到
10個(gè)任務(wù)分為兩批執(zhí)行,第一批和第二批的線程ID正好是一致的,而且前五個(gè)和后五個(gè)任務(wù)的時(shí)間正好相差一秒鐘.

  • 線程池的內(nèi)部實(shí)現(xiàn):
    根據(jù)上面三個(gè)簡(jiǎn)單線程池的代碼表面分析,都是使用ThreadPoolExecutor實(shí)現(xiàn).現(xiàn)在分析一下ThreadPoolExecutor;
/**
     * Creates a new {@code ThreadPoolExecutor} with the given initial
     * parameters.
     *
     * @param corePoolSize the number of threads to keep in the pool, even
     *        if they are idle, unless {@code allowCoreThreadTimeOut} is set
     * @param maximumPoolSize the maximum number of threads to allow in the
     *        pool
     * @param keepAliveTime when the number of threads is greater than
     *        the core, this is the maximum time that excess idle threads
     *        will wait for new tasks before terminating.
     * @param unit the time unit for the {@code keepAliveTime} argument
     * @param workQueue the queue to use for holding tasks before they are
     *        executed.  This queue will hold only the {@code Runnable}
     *        tasks submitted by the {@code execute} method.
     * @param threadFactory the factory to use when the executor
     *        creates a new thread
     * @param handler the handler to use when execution is blocked
     *        because the thread bounds and queue capacities are reached

*/
 public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler)
  • corePoolSize : 線程池中的線程數(shù)量
  • maximumPoolSize : 線程池中可以容納的最大線程數(shù)
  • keepAliveTime : 超過(guò)指定corePoolSize 的線程的存活時(shí)間.
  • unit : keepAliveTime的時(shí)間單位
  • workQueue : 沒(méi)有被提交的任務(wù)所存放的任務(wù)隊(duì)列.
  • ThreadFactory: 線程工廠
  • RejectedExecutionHandler : 拒絕策略, 當(dāng)任務(wù)來(lái)不及處理,如何拒絕任務(wù).

workQueue 沒(méi)有被提交的任務(wù)所存放的任務(wù)隊(duì)列 ,它是BlockingQueue接口的具體實(shí)現(xiàn),下面是對(duì)不同線程池的不同分析

通過(guò)看源碼可以得到:

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

newFixedThreadPool 該線程池的開(kāi)始線程數(shù)量和最大線程數(shù)量是一開(kāi)始就設(shè)置好的,并且使用了LinkedBlockingQueue 存放沒(méi)有被執(zhí)行的線程, LinkedBlockingQueue 是基于鏈表的實(shí)現(xiàn),適合做無(wú)界隊(duì)列,當(dāng)任務(wù)提交非常頻繁的時(shí)候, LinkedBlockingQueue 可能迅速膨脹.

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

newCachedThreadPool 該線程池的corePoolSize 是0, maximumPoolSize 為無(wú)窮大,意味著當(dāng)沒(méi)有任務(wù)提交時(shí),線程池里面沒(méi)有線程,當(dāng)有任務(wù)提交時(shí),會(huì)檢查是否有空閑線程,如果有的話就用空閑線程,如果沒(méi)有空閑線程,就會(huì)將任務(wù)加入 SynchronousQueue 隊(duì)列,
SynchronousQueue 是一種直接提交的隊(duì)列,它沒(méi)有容量,如果想要插入一個(gè)新的元素,就必須刪除一個(gè)元素,所以提交給他的任務(wù)不會(huì)真實(shí)的保存,而是直接扔給線程池執(zhí)行,當(dāng)任務(wù)執(zhí)行完畢后,由于corePoolSize = 0,所以空閑線程又會(huì)在指定的時(shí)間內(nèi)被回收.所以如果任務(wù)太多而當(dāng)任務(wù)執(zhí)行不夠快時(shí),他會(huì)開(kāi)啟等量的線程,這樣做線程池可能會(huì)開(kāi)啟大量的線程(后果自己想)

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

newSingleThreadExecutor 只是簡(jiǎn)單的將線程池線程數(shù)量設(shè)置為1,其余的和newFixedThreaPool 沒(méi)啥區(qū)別!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌虐呻,老刑警劉巖逻恐,帶你破解...
    沈念sama閱讀 210,835評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件把夸,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡因宇,警方通過(guò)查閱死者的電腦和手機(jī)翩剪,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,900評(píng)論 2 383
  • 文/潘曉璐 我一進(jìn)店門乳怎,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人前弯,你說(shuō)我怎么就攤上這事蚪缀〗垢ǎ” “怎么了?”我有些...
    開(kāi)封第一講書人閱讀 156,481評(píng)論 0 345
  • 文/不壞的土叔 我叫張陵椿胯,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我剃根,道長(zhǎng)哩盲,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書人閱讀 56,303評(píng)論 1 282
  • 正文 為了忘掉前任狈醉,我火速辦了婚禮廉油,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘苗傅。我一直安慰自己抒线,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,375評(píng)論 5 384
  • 文/花漫 我一把揭開(kāi)白布渣慕。 她就那樣靜靜地躺著嘶炭,像睡著了一般。 火紅的嫁衣襯著肌膚如雪逊桦。 梳的紋絲不亂的頭發(fā)上眨猎,一...
    開(kāi)封第一講書人閱讀 49,729評(píng)論 1 289
  • 那天,我揣著相機(jī)與錄音强经,去河邊找鬼睡陪。 笑死,一個(gè)胖子當(dāng)著我的面吹牛匿情,可吹牛的內(nèi)容都是我干的兰迫。 我是一名探鬼主播,決...
    沈念sama閱讀 38,877評(píng)論 3 404
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼炬称,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼汁果!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起玲躯,我...
    開(kāi)封第一講書人閱讀 37,633評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤须鼎,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后府蔗,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體晋控,經(jīng)...
    沈念sama閱讀 44,088評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,443評(píng)論 2 326
  • 正文 我和宋清朗相戀三年姓赤,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了赡译。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,563評(píng)論 1 339
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡不铆,死狀恐怖蝌焚,靈堂內(nèi)的尸體忽然破棺而出裹唆,到底是詐尸還是另有隱情,我是刑警寧澤只洒,帶...
    沈念sama閱讀 34,251評(píng)論 4 328
  • 正文 年R本政府宣布许帐,位于F島的核電站,受9級(jí)特大地震影響毕谴,放射性物質(zhì)發(fā)生泄漏成畦。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,827評(píng)論 3 312
  • 文/蒙蒙 一涝开、第九天 我趴在偏房一處隱蔽的房頂上張望循帐。 院中可真熱鬧,春花似錦舀武、人聲如沸拄养。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 30,712評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)瘪匿。三九已至,卻和暖如春寻馏,著一層夾襖步出監(jiān)牢的瞬間柿顶,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 31,943評(píng)論 1 264
  • 我被黑心中介騙來(lái)泰國(guó)打工操软, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留嘁锯,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,240評(píng)論 2 360
  • 正文 我出身青樓聂薪,卻偏偏與公主長(zhǎng)得像家乘,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子藏澳,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,435評(píng)論 2 348

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