通常開發者都是利用Executors提供的通用線程池創建方法,去創建不同配置的線程池,主要區別在於不同的
Executors目前提供了5種不同的線程池創建配置:
1、newCachedThreadPool(),它是用來處理大量短時間工作任務的線程池,具有幾個鮮明特點:它會試圖緩存線程並重用,當無緩存線程可用時,就會創建新的工作線程;如果線程閑置時間超過60秒,則被終止並移除緩存;長時間閑置時,這種線程池,不會消耗什么資源。其內部使用SynchronousQueue作為工作隊列。
2、newFixedThreadPool(int nThreads),重用指定數目(nThreads)的線程,其背后使用的是無界的工作隊列,任何時候最多有nThreads個工作線程是活動的。這意味着,如果任務數量超過了活動線程數目,將在工作隊列中等待空閑線程出現;如果工作線程退出,將會有新的工作線程被創建,以補足指定數目nThreads。
3、newSingleThreadExecutor(),它的特點在於工作線程數目限制為1,操作一個無界的工作隊列,所以它保證了所有的任務都是被順序執行,最多會有一個任務處於活動狀態,並且不予許使用者改動線程池實例,因此可以避免改變線程數目。
4、newSingleThreadScheduledExecutor()和newScheduledThreadPool(int corePoolSize),創建的是個ScheduledExecutorService,可以進行定時或周期性的工作調度,區別在於單一工作線程還是多個工作線程。
5、newWorkStealingPool(int parallelism),這是一個經常被人忽略的線程池,Java 8 才加入這個創建方法,其內部會構建ForkJoinPool,利用Work-Stealing算法,並行地處理任務,不保證處理順序。
Executor框架的基本組成
各個類型的設計目的
1、Executor是一個基礎的接口,其初衷是將任務提交和任務執行細節解耦,這一點可以天匯其定義的唯一方法。
void execute(Runnable command);
2、ExecutorService則更加完善,不僅提供service管理,比如shutdown等方法,也提供了更加全面的提交任務機制,如返回Future而不是 void 的 submit 方法
<T> Future<T> submit(Callable<T> task);
注意,這個例子輸入的可是Callable,它解決了Runnable無法返回結果的困擾。
3、Java標准類庫提供了幾種基礎實現,比如 ThreadPoolExecutor、ScheduledThreadPoolExecutor、ForkJoinPool。這些線程池設的設計特點在於其高度的可調節性和靈活性,以盡量滿足復雜多變的實際應用場景,我會進一步分析其構建部分的源碼,剖析這種靈活性的源頭。
4、Executors則從簡化使用的角度,為我們提供了各種方便的靜態工長方法。
阿里發布的 Java開發手冊中強制線程池不允許使用 Executors 去創建,而是通過 ThreadPoolExecutor 的方式,這樣的處理方式讓寫的同學更加明確線程池的運行規則,規避資源耗盡的風險
下面從源碼角度,分析線程池的設計與實現,將主要圍繞最基礎的ThreadPoolExecutor源碼。ScheduledThreadPoolExecutor是ThreadPoolExecutor的擴展,主要是增加了調度邏輯。而ForkJoinPool則是為了ForkJoinTask定制的線程池,與通常意義的線程池有所不同。
在現實應用中,理解應用於線程池的交互和線程池內部的工作過程,可以參考下圖。
簡單理解一下:
1、工作隊列負責存儲用戶提交的各個任務,這個工作隊列,可以是容量為0的 SynchronousQueue(使用newCachedThreadPool),也可以是像固定大小線程池(newFixedThreadPool)那樣使用LinkedBlockingQueue。
private final BlockingQueue<Runnable> workQueue;
2、內部的“線程池”,這是指保持工作線程的集合,線程池需要在運行過程中管理線程創建、銷毀。例如,對於帶緩存的線程池,當任務壓力較大時,線程池會創建新的工作線程;當業務壓力退去,線程池會閑置一段時間(默認60秒)后結束線程。
private final HashSet<Worker> workers = new HashSet<Worker>();
線程池的工作線程被抽象為靜態內部類Worker,基於AQS實現
3、ThreadFactory提供上面所需要的創建線程邏輯。
4、如果任務提交時被拒絕,比如線程池已處於SHUTDOWN狀態,需要為其提供處理邏輯,Java 標准庫提供了類似 ThreadPoolExecutor.AbortPolicy 等默認實現,也可以按照實際需要自定義。
從上面的分析,就可以看出線程池的幾個基本組成部分,一起都體現在線程池的構造函數中,從字面我們就可以猜測到其用意:
1、corePoolSize, 稍微的核心線程數,可以大致理解為長期駐留的線程數目(除非設置了allowCoreThreadTimeOut)。對於不同的線程池,這個值可能會有很大區別,比如newFixedThreadPool 會將其設置為 nThreads ,而對於newCachedThreadPool則設為0。
2、maximumPoolSize,顧名思義,就是線程不夠時能夠創建的最大線程數。同樣進行對比,對於newFixedThreadPool,當然就是nThreads,以為其要求是固定大小,而newCachedThreadPool則是Integer.VALUE。
3、keepAliveTime和TimeUnit,這兩個參數指定了額外的線程能夠閑置多久,顯然有些線程池不需要它。
4、workQueue,工作隊列,必須是BlockingQueue。
通過配置不同的參數,我們就可以創建出行為大相徑庭的線程池,這就是線程池高度靈活性的基礎。
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
RejectedExecutionHandler handler) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), handler);
}