1. 程式人生 > >ThreadPoolExecutor(執行緒池的構建)

ThreadPoolExecutor(執行緒池的構建)

首先整理一下執行緒池的一些概念
執行緒池的作用:
執行緒池作用就是限制系統中執行執行緒的數量。
根據系統的環境情況,可以自動或手動設定執行緒數量,達到執行的最佳效果;少了浪費了系統資源,多了造成系統擁擠效率不高。用執行緒池控制執行緒數量,其他執行緒 排隊等候。一個任務執行完畢,再從佇列的中取最前面的任務開始執行。若佇列中沒有等待程序,執行緒池的這一資源處於等待。當一個新任務需要執行時,如果執行緒 池中有等待的工作執行緒,就可以開始運行了;否則進入等待佇列。

為什麼要用執行緒池:
減少了建立和銷燬執行緒的次數,每個工作執行緒都可以被重複利用,可執行多個任務
可以根據系統的承受能力,調整執行緒池中工作線執行緒的數目,防止因為因為消耗過多的記憶體,而把伺服器累趴下(每個執行緒需要大約1MB記憶體,執行緒開的越多,消耗的記憶體也就越大,最後宕機)

使用Executors作為一個工具類可以用來構建執行緒池。
首先來看ThreadPoolExecutor 構造器:
這裡寫圖片描述

七個引數:

corePoolSize:核心池的大小,在建立了執行緒池後,預設情況下,執行緒池中並沒有任何執行緒,而是等待有任務到來才建立執行緒去執行任務,當有任務來之後,就會建立一個執行緒去執行任務,當執行緒池中的執行緒數目達到corePoolSize後,就會把到達的任務放到快取隊列當中;
maximumPoolSize:執行緒池最大執行緒數;
keepAliveTime:表示執行緒沒有任務執行時最多保持多久時間會終止;
unit:引數keepAliveTime的時間單位(DAYS、HOURS、MINUTES、SECONDS 等);
workQueue

:阻塞佇列,用來儲存等待執行的任務;
ArrayBlockingQueue (有界佇列)
LinkedBlockingQueue (無界佇列)
SynchronousQueue (無快取佇列)
threadFactory:執行緒工廠,主要用來建立執行緒
handler:拒絕處理任務的策略
AbortPolicy:丟棄任務並丟擲 RejectedExecutionException 異常。(預設這種)
DiscardPolicy:也是丟棄任務,但是不丟擲異常
DiscardOldestPolicy:丟棄佇列最前面的任務,然後重新嘗試執行任務(重複此過程)
CallerRunsPolicy:由呼叫執行緒處理該任務

看這個引數很容易讓人以為是執行緒池裡保持corePoolSize個執行緒,如果不夠用,就加執行緒入池直至maximumPoolSize大小,如果還不夠就往workQueue里加,如果workQueue也不夠就用RejectedExecutionHandler來做拒絕處理。

但實際情況不是這樣,具體流程如下

1)當池子大小小於corePoolSize就新建執行緒,並處理請求

2)當池子大小等於corePoolSize,把請求放入workQueue中,池子裡的空閒執行緒就去從workQueue中取任務並處理

3)當workQueue放不下新入的任務時,新建執行緒入池,並處理請求,如果池子大小撐到了maximumPoolSize就用RejectedExecutionHandler來做拒絕處理

4)另外,當池子的執行緒數大於corePoolSize的時候,多餘的執行緒會等待keepAliveTime長的時間,如果無請求可處理就自行銷燬

可以建立以下4各型別的執行緒池
1.newCachedThreadPool

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

構造一個緩衝功能的執行緒池,配置corePoolSize=0,maximumPoolSize=Integer.MAX_VALUE,keepAliveTime=60s,以及一個無容量的阻塞佇列 SynchronousQueue,因此任務提交之後,將會建立新的執行緒執行;執行緒空閒超過60s將會銷燬

2.newFixedThreadPool

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

構造一個固定執行緒數目的執行緒池,配置的corePoolSize與maximumPoolSize大小相同,同時使用了一個無界LinkedBlockingQueue存放阻塞任務,因此多餘的任務將存在再阻塞佇列,不會由RejectedExecutionHandler處理

3.newSingleThreadExecutor

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

構造一個只支援一個執行緒的執行緒池,配置corePoolSize=maximumPoolSize=1,無界阻塞佇列LinkedBlockingQueue;保證任務由一個執行緒序列執行

4.newScheduledThreadPool

    public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
        return new ScheduledThreadPoolExecutor(corePoolSize);
    }

構造有定時功能的執行緒池,配置corePoolSize,無界延遲阻塞佇列DelayedWorkQueue;有意思的是:maximumPoolSize=Integer.MAX_VALUE,由於DelayedWorkQueue是無界佇列,所以這個值是沒有意義的

我理解到的執行緒池會建立執行緒的條件
1.當執行緒數小於核心執行緒數時,建立執行緒。
2.當執行緒數大於等於核心執行緒數,且任務佇列未滿時,將任務放入任務佇列。
3.當執行緒數大於等於核心執行緒數,且任務佇列已滿
a.若執行緒數小於最大執行緒數,建立執行緒
b.若執行緒數等於最大執行緒數,丟擲異常,拒絕任務
注:建立固定大小執行緒池時即newFixedThreadPool時,因為使用的是無序佇列所以到2是再進任務會一致重複2的操作。