Java線程池

簡述

在面向軟件編程中涩金,創(chuàng)建和銷毀對象是一件非常耗時的事情筐带,因為創(chuàng)建一個對象要獲取內(nèi)存資源或者其它更多的資源泽论。在Java中更是如此桐罕,虛擬機將試圖跟蹤每一個對象默怨,以便能在對象銷毀時進行回收。所以提供程序效率的方法就是減少對象的創(chuàng)建和銷毀久锥。如何利用已有的對象來服務(wù)就是一個需要解決的問題家淤。

Java線程池實現(xiàn)了一個Java高并發(fā)的、Java多線程的瑟由、可管理的統(tǒng)一調(diào)度器絮重。java.util.concurrent.Executors工作中最常用的和熟知的,順便一提歹苦,java.util.concurrent包由出自著名的大神Doug Lea之手青伤。

Executors是個線程工廠類,方便快速地創(chuàng)建很多線程池殴瘦,也可以說是一個線程池工具類狠角。配置一個線程池是比較復(fù)雜的,尤其是對于線程池原理不是很清楚的情況下蚪腋,很可能配置的線程池不是最優(yōu)的丰歌,以至于達不到預(yù)期的效果,因此Executors類里面給我們提供了一些靜態(tài)工廠方法屉凯,以便我們能生成常用的線程池立帖。
常用的方法有如下三種:

  • 1、newSingleThreadExecutor:創(chuàng)建一個單線程的線程池悠砚。
  • 2晓勇、newFixedThreadPool:創(chuàng)建固定大小的線程池。
  • 3、newCachedThreadPool:創(chuàng)建一個可緩存的線程池绑咱。
1绰筛、newSingleThreadExecutor的使用

創(chuàng)建一個單線程的線程池。這個線程只有一個線程在工作描融,也就是相當(dāng)于單線程串行執(zhí)行所有任務(wù)别智。如果這個唯一的線程因為異常而結(jié)束,那么會有一個新的線程來代替它稼稿。此線程保證所有的任務(wù)的執(zhí)行順序按照任務(wù)的提交順序執(zhí)行薄榛。

public class SingleThreadExecutorDemo {
    public static void main(String[] args) {
        ExecutorService executorService=Executors.newSingleThreadExecutor();
        for (int i = 0; i < 10; i++) {
            final int no=i;
            Runnable task=new Runnable() {
                
                @Override
                public void run() {
                    try{
                        System.out.println("into "+no);
                        Thread.sleep(1000L);
                        System.out.println("end "+no);
                    }catch(InterruptedException e){
                        e.printStackTrace();
                    }
                }
            };
            //交由線程池處理任務(wù)
            executorService.execute(task);
        }
        executorService.shutdown();
        System.out.println("main thread have terminate");
    }   
}

運行結(jié)果如下所示:

newSingleThreadExecutor運行結(jié)果

從截圖中可以得知任務(wù)是一條一條的執(zhí)行的。

查看一下Executors.newSingleThreadExecutor()的實現(xiàn)方法:

    /**
     * Creates an Executor that uses a single worker thread operating
     * off an unbounded queue. (Note however that if this single
     * thread terminates due to a failure during execution prior to
     * shutdown, a new one will take its place if needed to execute
     * subsequent tasks.)  Tasks are guaranteed to execute
     * sequentially, and no more than one task will be active at any
     * given time. Unlike the otherwise equivalent
     * <tt>newFixedThreadPool(1)</tt> the returned executor is
     * guaranteed not to be reconfigurable to use additional threads.
     *
     * @return the newly created single-threaded Executor
     */
    public static ExecutorService newSingleThreadExecutor() {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>()));
    }

通過源碼得知让歼,它是調(diào)用了ThreadPoolExecutor創(chuàng)建了一個LinkedBlockingQueue的一個大小的線程池敞恋,采用默認(rèn)的異常策略。

2谋右、newCachedThreadPool的使用

創(chuàng)建一個緩沖池大小可根據(jù)需要伸縮的線程池硬猫,但是在以前構(gòu)造的線程可用時將重用它們。對于執(zhí)行很多短期異步任務(wù)而言改执,這些線程池通承ッ郏可提供程序性能。調(diào)用execute將重用以前構(gòu)造的線程(如果線程可用)辈挂。如果現(xiàn)有線程沒有可用的衬横,則創(chuàng)建一個新線程并添加到池中。終止并從緩存中移除那些已有60s未被使用的線程终蒂。因此蜂林,長時間保持空閑的線程池不會使用任何資源。
Demo:

public class CachedThreadPoolDemo {
    public static void main(String[] args) {
        ExecutorService executorService=Executors.newCachedThreadPool();
        for (int i = 0; i < 20; i++) {
            final int no=i;
            Runnable task=new Runnable() {
                @Override
                public void run() {
                    try{
                        System.out.println("into "+no);
                        Thread.sleep(10001L);
                        System.out.println("end "+no);
                    }catch(InterruptedException e){
                        e.printStackTrace();
                    }
                }
            };
            executorService.execute(task);
        }
        System.out.println("main thread have terminate");
        executorService.shutdown();
    }
}   
newCachedThreadPool運行結(jié)果

從運行結(jié)果可知拇泣,任務(wù)一開始所有的線程就開始執(zhí)行了噪叙,都在互相爭奪CPU資源。

接下來我們看下它的源碼實現(xiàn):

    /**
     * Creates a thread pool that creates new threads as needed, but
     * will reuse previously constructed threads when they are
     * available.  These pools will typically improve the performance
     * of programs that execute many short-lived asynchronous tasks.
     * Calls to <tt>execute</tt> will reuse previously constructed
     * threads if available. If no existing thread is available, a new
     * thread will be created and added to the pool. Threads that have
     * not been used for sixty seconds are terminated and removed from
     * the cache. Thus, a pool that remains idle for long enough will
     * not consume any resources. Note that pools with similar
     * properties but different details (for example, timeout parameters)
     * may be created using {@link ThreadPoolExecutor} constructors.
     *
     * @return the newly created thread pool
     */
    public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());
    }

代碼創(chuàng)建了一個內(nèi)核線程池霉翔。線程為零睁蕾,來一個線程就在線程池里面創(chuàng)建一個SynchronousQueue。

3债朵、newFixedThreadPool的使用

創(chuàng)建一個可重用固定線程數(shù)的線程池子眶,以共享的無界隊列方式來運行這些線程。在任意點葱弟,在大多數(shù)nThreads線程會處于處理任務(wù)的活動狀態(tài)壹店。如果在所有線程處于活動狀態(tài)時提交附加任務(wù),則在有可用線程之前芝加,附加任務(wù)將在隊列中等待。如果在關(guān)閉前的執(zhí)行期間由于失敗而導(dǎo)致任何線程終止,那么一個新的線程將代替它執(zhí)行后續(xù)任務(wù)(如果需要)藏杖。在某個線程被顯示關(guān)閉之前将塑,池中的線程將一直存在。
Demo:

public class newFixedThreadPoolDemo {
    public static void main(String[] args) {
        ExecutorService executorService=Executors.newFixedThreadPool(5);
        for (int i = 0; i < 20; i++) {
            final int no=i;
            Runnable task=new Runnable() {
                @Override
                public void run() {
                    try{
                        System.out.println("into "+no);
                        Thread.sleep(1000L);
                        System.out.println("end "+no);
                    }catch(InterruptedException e){
                        e.printStackTrace();
                    }
                }
            };
            executorService.execute(task);
        }
        System.out.println("main thread have terminate");
        executorService.shutdown();
    }
}

運行結(jié)果如下:

newFixedThreadPool運行結(jié)果

從結(jié)果上面看蝌麸,一下子只有5個線程同時執(zhí)行点寥,然后結(jié)束一個再執(zhí)行一個。

接下來看下源碼實現(xiàn):

    /**
     * Creates a thread pool that reuses a fixed number of threads
     * operating off a shared unbounded queue.  At any point, at most
     * <tt>nThreads</tt> threads will be active processing tasks.
     * If additional tasks are submitted when all threads are active,
     * they will wait in the queue until a thread is available.
     * If any thread terminates due to a failure during execution
     * prior to shutdown, a new one will take its place if needed to
     * execute subsequent tasks.  The threads in the pool will exist
     * until it is explicitly {@link ExecutorService#shutdown shutdown}.
     *
     * @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>());

代碼創(chuàng)建了一個指定大小的LinkedBlockingQueue的線程池来吩。

線程池的好處
1敢辩、合理利用線程能帶來4個好處

(1). 降低資源消耗。通過重復(fù)利用已創(chuàng)建的線程弟疆,降低線程創(chuàng)建和銷毀造成的消耗戚长。
(2). 提供響應(yīng)速度。當(dāng)任務(wù)到達時怠苔,任務(wù)可以不需要等待線程創(chuàng)建就能立即執(zhí)行同廉。
(3).提供線程的可管理性。線程是稀缺資源柑司,如果無限制的創(chuàng)建迫肖,不僅會消耗系統(tǒng)資源,還會降低系統(tǒng)的穩(wěn)定性攒驰,使用線程池可以進行統(tǒng)一分配蟆湖、調(diào)優(yōu)和監(jiān)控。但是要做到合理地利用線程玻粪,必須對其原理了如指掌帐姻。
(4).防止服務(wù)器過載,形成內(nèi)存溢出奶段,或者CPU耗盡饥瓷。

2、線程池技術(shù)如何高服務(wù)器程序的性能

這里所提及的服務(wù)器程序是指能夠接收客戶端請求并處理請求的程序痹籍,而不只是那些接受網(wǎng)絡(luò)客戶端請求的網(wǎng)絡(luò)服務(wù)器程序呢铆。多線程技術(shù)主要解決處理器單元內(nèi)多個線程執(zhí)行的問題,它可以顯著減少處理器單元的閑置時間蹲缠,增加處理器單元的吞吐能力棺克。但如果對多線程應(yīng)用不當(dāng),會增加對單個任務(wù)的處理時間线定。

3娜谊、舉個栗子

假設(shè)在一臺服務(wù)器完成一項任務(wù)的時間為T,并假設(shè):

  • T1斤讥,創(chuàng)建線程的時間纱皆。

  • T2,在線程中執(zhí)行任務(wù)所花費的時間,包括線程同步所需時間

  • T3派草,線程銷毀時間
    顯然T=T1+T2+T3搀缠。這是一個極度簡化的假設(shè)栗子。
    可以看出T1近迁、T3是多線程本身帶來的開銷艺普,我們渴望減少T1、T3所用的時間從而減少T的時間鉴竭。但一些線程的使用者并沒有注意到這一點歧譬,所以在程序中頻繁的創(chuàng)建和銷毀線程,這導(dǎo)致T1和T3在T中占有相當(dāng)比例搏存。顯然這是突出了線程的弱點(T1瑰步、T3),而不是優(yōu)點(并發(fā)性)祭埂。

            線程池技術(shù)正是關(guān)注如何縮短調(diào)整T1面氓、T3時間的技術(shù),從而提高服務(wù)器程序性能的蛆橡。它把T1舌界、T3分別安排在服務(wù)器程序啟動或結(jié)束的時間段或者一些空閑的時間段,這樣在服務(wù)器程序處理客戶端請求時泰演,不會有T1呻拌、T3的開銷了。
    

線程池不僅調(diào)整了T1睦焕、T3產(chǎn)生的時間段藐握,而且它還顯著減少了創(chuàng)建線程的數(shù)目。再看一個栗子:

假設(shè)一個服務(wù)器一天要處理50000個請求垃喊,并且每個請求需要一個單獨的線程數(shù)目完成猾普。我們比較一下利用線程技術(shù)和不利用線程池技術(shù)的服務(wù)器處理這些請求時所產(chǎn)生的線程總數(shù)。在線程池中本谜,線程數(shù)一般是固定的初家,所以產(chǎn)生線程的總數(shù)不會超過線程池中的數(shù)目或者上限(以下簡稱線程池尺寸),而如果服務(wù)器不利用線程池來處理這些請求則線程總數(shù)為50000.一般線程池尺寸是遠(yuǎn)遠(yuǎn)小于50000的乌助。所以利用線程池的服務(wù)器程序不會為了創(chuàng)建50000而在處理請求時浪費時間溜在,從而提高整體效率。

4他托、線程池應(yīng)用范圍

(1)需要大量的線程來完成任務(wù)掖肋,且完成任務(wù)的時間比較短。Web服務(wù)器完成網(wǎng)頁請求這樣的任務(wù)赏参,使用多線程池技術(shù)是非常合適的志笼。因為單個任務(wù)小沿盅,而任務(wù)數(shù)量巨大,你可以想象一個熱門網(wǎng)站的點擊次數(shù)籽腕。但對于長時間的任務(wù)嗡呼,比如一個Telnet連接請求纸俭,線程池的優(yōu)點就不明顯了皇耗。因為Telnet回話時間比線程的創(chuàng)建時間打多了。
(2)對性能要求苛刻的應(yīng)用揍很,比如要求服務(wù)器迅速響應(yīng)客戶端請求郎楼。
(3)接收突發(fā)性的大量請求,但不至于使用服務(wù)器因此產(chǎn)生大量線程的應(yīng)用窒悔。突發(fā)性大量客戶請求呜袁,在沒有線程池的情況下,將產(chǎn)生大量的線程简珠,雖然理論上大部分操作系統(tǒng)線程數(shù)目最大值不是問題阶界,短時間內(nèi)產(chǎn)生大量線程可能使內(nèi)存到達極限,并出現(xiàn)OutOfMemory的錯誤聋庵。

The end: next: 線程池工作機制與原理

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末膘融,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子祭玉,更是在濱河造成了極大的恐慌氧映,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,039評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件脱货,死亡現(xiàn)場離奇詭異岛都,居然都是意外死亡,警方通過查閱死者的電腦和手機振峻,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,426評論 3 395
  • 文/潘曉璐 我一進店門臼疫,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人扣孟,你說我怎么就攤上這事烫堤。” “怎么了哈打?”我有些...
    開封第一講書人閱讀 165,417評論 0 356
  • 文/不壞的土叔 我叫張陵塔逃,是天一觀的道長。 經(jīng)常有香客問我料仗,道長湾盗,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,868評論 1 295
  • 正文 為了忘掉前任立轧,我火速辦了婚禮格粪,結(jié)果婚禮上躏吊,老公的妹妹穿的比我還像新娘。我一直安慰自己帐萎,他們只是感情好比伏,可當(dāng)我...
    茶點故事閱讀 67,892評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著疆导,像睡著了一般赁项。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上澈段,一...
    開封第一講書人閱讀 51,692評論 1 305
  • 那天悠菜,我揣著相機與錄音,去河邊找鬼败富。 笑死悔醋,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的兽叮。 我是一名探鬼主播芬骄,決...
    沈念sama閱讀 40,416評論 3 419
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼鹦聪!你這毒婦竟也來了账阻?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,326評論 0 276
  • 序言:老撾萬榮一對情侶失蹤椎麦,失蹤者是張志新(化名)和其女友劉穎宰僧,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體观挎,經(jīng)...
    沈念sama閱讀 45,782評論 1 316
  • 正文 獨居荒郊野嶺守林人離奇死亡琴儿,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,957評論 3 337
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了嘁捷。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片造成。...
    茶點故事閱讀 40,102評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖雄嚣,靈堂內(nèi)的尸體忽然破棺而出晒屎,到底是詐尸還是另有隱情,我是刑警寧澤缓升,帶...
    沈念sama閱讀 35,790評論 5 346
  • 正文 年R本政府宣布鼓鲁,位于F島的核電站,受9級特大地震影響港谊,放射性物質(zhì)發(fā)生泄漏骇吭。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,442評論 3 331
  • 文/蒙蒙 一歧寺、第九天 我趴在偏房一處隱蔽的房頂上張望燥狰。 院中可真熱鬧棘脐,春花似錦、人聲如沸龙致。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,996評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽目代。三九已至屈梁,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間像啼,已是汗流浹背俘闯。 一陣腳步聲響...
    開封第一講書人閱讀 33,113評論 1 272
  • 我被黑心中介騙來泰國打工潭苞, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留忽冻,地道東北人。 一個月前我還...
    沈念sama閱讀 48,332評論 3 373
  • 正文 我出身青樓此疹,卻偏偏與公主長得像僧诚,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子蝗碎,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,044評論 2 355

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