線程池原理及其使用

線程池

線程池的優點

  • 重復利用已經創建的線程,減少創建線程和銷毀線程的開銷
  • 提高響應速度,不需要等到線程創建就能立即執行
  • 使用線程池可以進行統一分配,調優和監控
  • 總的來說:降低資源消耗,提高響應速度,提高線程可管理性

線程池原理

  • 提交任務
  • 核心線程池(corePoolSize)是否已經滿,如果未滿的話就創建線程執行任務
  • 否則查看隊列(BlockingQueue)是否已滿,未滿的話,將任務存儲在隊列里
  • 如果已經滿了,看線程池(maximumPoolSize)是否已滿,如果滿的話按照拒絕處理任務策略(handler)處理無法執行的任務
  • 如果未滿,創建線程執行任務

ThreadPoolExecutor構造參數

  • corePoolSize:核心池的大小,構建線程池后,并不會創建線程,當前線程數如果小于corePoolSize時,當要執行任務時,創建一個線程。當當前線程數等于corePoolSize,會將任務放入隊列中
  • maximumPoolSize:線程池最大數,也就是線程最多能創建的線程
  • keepAliveTime:工作線程空閑后,保持存活的時間。默認情況下,如果當前線程數大于corePoolSize,那么一個線程如果沒有任務,當空閑的時間大于keepAliveTime時,會終止該線程,直到線程數不超過corePoolSize
  • workQueue:存儲任務的隊列,有幾種種類型隊列ArrayBlockingQueue(有界緩沖區,基于數組的隊列,先進先出,必須指定大小,可以設置是否保持公平,以FIFO順序訪問),LinkedBlockingQueue(基于鏈表的隊列,如果沒有指定大小,默認為Integer.MAX_VALUE),SynchronousQueue(無界線程池,不管多少任務提交進來,直接運行)
  • ThreadFactory:線程工廠,用來創建線程,通過線程工廠可以給創建的線程設置名字
  • rejectedExecutionHandler:拒絕處理任務的策略AbortPolicy(直接放棄任務,拋出RejectedExecutionException異常),DiscardPolicy(放棄任務,不拋出異常),DiscardOldestPolicy(放棄最舊的未處理請求,然后重試 execute;如果執行程序已關閉,則會丟棄該任務),CallerRunsPolicy(它直接在 execute 方法的調用線程中運行被拒絕的任務;如果執行程序已關閉,則會丟棄該任務)

ThreadPoolExecutor重要方法

  • execute:在將來某個時間執行給定任務
  • submit:提交一個任務用于執行,并返回一個表示該任務的 Future,和execute不同的是返回的是一個Future,可以在任務執行完畢之后得到任務執行結果
  • shutdown:按過去執行已提交任務的順序發起一個有序的關閉,但是不接受新任務。也就是說,中斷沒有正在執行任務的線程,等待任務執行完畢。
  • shutdownnow: 嘗試停止所有的活動執行任務、暫停等待任務的處理,并返回等待執行的任務列表。

線程池狀態

volatile int runState;
static final int RUNNING    = 0;
static final int SHUTDOWN   = 1;
static final int STOP       = 2;
static final int TERMINATED = 3;
  • RUNNING:創建線程池后,初始狀態為RUNNING
  • SHUTDOWN:執行shutdown方法后,線程池處于SHUTDOWN狀態
  • STOP:執行shutdownNow方法后,線程池處于STOP狀態
  • TERMINATED:當線程池處于SHUTDOWN或STOP狀態,并且所有工作線程已經銷毀,任務緩存隊列已經清空或執行結束后,線程池被設置為TERMINATED狀態。

源碼分析

public void execute(Runnable command) {
    if (command == null)
        throw new NullPointerException();
    if (poolSize >= corePoolSize || !addIfUnderCorePoolSize(command)) {
        if (runState == RUNNING && workQueue.offer(command)) {
            if (runState != RUNNING || poolSize == 0)
                ensureQueuedTaskHandled(command);
        }
        else if (!addIfUnderMaximumPoolSize(command))
            reject(command); // is shutdown or saturated
    }
}
  • command是否為空,是的話拋出異常
  • 如果當前線程是否小于corePoolSize,執行addIfUnderCorePoolSize方法,創建線程并執行任務
  • 如果當前線程線程數大于等于corePoolSize或者執行addIfUnderCorePoolSize返回false,將當前任務放到隊列中
  • 如果線程池狀態不是RUNNING狀態或者任務無法加入到隊列,并且線程數大于最大線程數,執行reject方法
  • 流程跟上面的線程池原理相同

配置線程池

  • cpu密集型和io密集型線程數的選擇,cpu密集型不需要太多的線程,可以充分利用cpu資源,io密集型適當多線程,io阻塞時可以切換至另一線程。
  • 優先級不同的的任務可以使用PriorityBlockingQueue來處理
  • 建議使用有界隊列,能夠增加系統的穩定性(如果使用無界隊列,當出現問題時候,隊列無限增長,此時可能會占用大量內存,導致系統出現問題)和預警能力(當出現隊列占滿的時候,拋出異常,可以讓開發人員及時發現)

線程池使用

public static void main(String[] args)
    {
        
        final Vector<Integer> vector = new Vector<Integer>();
        //corePoolSize,maximumPoolSize,keepAliveTime,TimeUnit,BlockingQueue
        ThreadPoolExecutor tp = new ThreadPoolExecutor(5, 10, 60, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(10));
        final Random random = new Random();
        System.out.println(tp.getPoolSize());
        for (int i = 0; i < 20; i++)
        {
            tp.execute(new Runnable()
            {
                public void run()
                {
                    vector.add(random.nextInt());
                    
                }
            });
        }
        
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            // TODO 自動生成的 catch 塊
            e.printStackTrace();
        }
        tp.shutdown();
        System.out.println("已完成的任務:"+tp.getCompletedTaskCount());
        System.out.println("活動的線程數:"+tp.getActiveCount());
        System.out.println("list大小:"+vector.size());
    }
  • 上面的代碼我們自己構建了一個ThreadPoolExecutor,而Executor給我們提供了幾個靜態方法用于創建線程池,本質上只是設置了給定的參數而已
public static ExecutorService newFixedThreadPool(int nThreads) {
    return new ThreadPoolExecutor(nThreads, nThreads,
                                  0L, TimeUnit.MILLISECONDS,
                                  new LinkedBlockingQueue<Runnable>());
}
  • FixedThreadPool,顧名思義,一個固定大小的線程池,隊列使用的無限制大小的鏈表阻塞隊列
public static ExecutorService newSingleThreadExecutor() {
    return new FinalizableDelegatedExecutorService
        (new ThreadPoolExecutor(1, 1,
                                0L, TimeUnit.MILLISECONDS,
                                new LinkedBlockingQueue<Runnable>()));
}
  • 單線程線程池,同樣隊列使用的無限制大小的鏈表阻塞隊列
public static ExecutorService newCachedThreadPool() {
    return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                  60L, TimeUnit.SECONDS,
                                  new SynchronousQueue<Runnable>());
}
  • 無界線程池,無論多少任務,直接運行,如果線程池長度超過處理需要,可靈活回收空閑線程,若無可回收,則新建線程

我覺得分享是一種精神,分享是我的樂趣所在,不是說我覺得我講得一定是對的,我講得可能很多是不對的,但是我希望我講的東西是我人生的體驗和思考,是給很多人反思,也許給你一秒鐘、半秒鐘,哪怕說一句話有點道理,引發自己內心的感觸,這就是我最大的價值。(這是我喜歡的一句話,也是我寫博客的初衷)

作者:jiajun 出處: http://www.cnblogs.com/-new/

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,835評論 6 534
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,676評論 3 419
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,730評論 0 380
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,118評論 1 314
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,873評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,266評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,330評論 3 443
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,482評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,036評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,846評論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,025評論 1 371
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,575評論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,279評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,684評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,953評論 1 289
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,751評論 3 394
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,016評論 2 375

推薦閱讀更多精彩內容