JUC學習系列二(線程池Executors與ThreadPoolExecutor)

概念說明

 

Executor:一個接口,其定義了一個接收Runnable對象的方法executor,其方法簽名爲executor(Runnable command),
ExecutorService:是一個比Executor使用更廣泛的子類接口,其提供了生命週期管理的方法,以及可跟蹤一個或多個異步任務執行狀況返回Future的方法AbstractExecutorService:ExecutorService執行方法的默認實現
ScheduledExecutorService:一個可定時調度任務的接口
ScheduledThreadPoolExecutor:ScheduledExecutorService的實現,一個可定時調度任務的線程池
ThreadPoolExecutor:線程池,可以通過調用Executors以下靜態工廠方法來創建線程池並返回一個ExecutorService對象

Executor是用來執行提交的Runnable任務的對象,並以接口的形式定義,提供一種提交任務(submission task)與執行任務(run task)之間的解耦方式,還包含有線程使用與週期調度的詳細細節等。

ExecutorService接口擴展了Executor,提供管理線程池終止的一組方法,還提供了產生Future的方法,Future是用於追蹤一個或多個異步任務的對象,並能返回異步任務的計算結果。

一、ThreadPoolExecutor

簡介:ThreadExecutorPool是使用最多的線程池組件,瞭解它的原始資料最好是從從設計者(Doug Lea)的口中知道它的來龍去脈。在Jdk1.6中,ThreadPoolExecutor直接繼承了AbstractExecutorService, 並層級實現了ExecutorService和Executor接口。

線程池處理了兩個問題:1.改善了性能。當執行大量的異步任務時,減少了每次調用的開銷。2.提供了一直資源管理的方式。線程池內部的線程可以得到有效的複用。每個ThreadPoolExecutor內部還維護了一些基礎的統計量,如完成的任務總數.ThreadPoolExecutor在很多場景下都有其用武之地,它提供了很多可調整的參數與可擴展的鉤子方法


  public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler) {
        if (corePoolSize < 0 ||
            maximumPoolSize <= 0 ||
            maximumPoolSize < corePoolSize ||
            keepAliveTime < 0)
            throw new IllegalArgumentException();
        if (workQueue == null || threadFactory == null || handler == null)
            throw new NullPointerException();
        this.acc = System.getSecurityManager() == null ?
                null :
                AccessController.getContext();
        this.corePoolSize = corePoolSize;
        this.maximumPoolSize = maximumPoolSize;
        this.workQueue = workQueue;
        this.keepAliveTime = unit.toNanos(keepAliveTime);
        this.threadFactory = threadFactory;
        this.handler = handler;
    }

參數說明:   

corePoolSize                         核心線程池大小
maximumPoolSize                 線程池最大容量大小
keepAliveTime                     線程池空閒時,線程存活的時間
TimeUnit                              時間單位
ThreadFactory                     線程工廠
BlockingQueue                    任務隊列
RejectedExecutionHandler  線程拒絕策略

其中比較容易讓人誤解的是:corePoolSize,maximumPoolSize,workQueue之間關係。
1.當線程池小於corePoolSize時,新提交任務將創建一個新線程執行任務,即使此時線程池中存在空閒線程。
2.當線程池達到corePoolSize時,新提交任務將被放入workQueue中,等待線程池中任務調度執行
3.當workQueue已滿,且maximumPoolSize>corePoolSize時,新提交任務會創建新線程執行任務
4.當提交任務數超過maximumPoolSize時,新提交任務由RejectedExecutionHandler處理
5.當線程池中超過corePoolSize線程,空閒時間達到keepAliveTime時,關閉空閒線程
6.當設置allowCoreThreadTimeOut(true)時,線程池中corePoolSize線程空閒時間達到keepAliveTime也將關閉
 
ThreadPoolExecutor會自動調節池子裏線程的大小
      通過execute方法提交新任務時,如果當前的池子裏線程的大小小於核心線程corePoolSize時,則會創建新線程來處理新的請求,即使當前的工作者線程是空閒的。如果運行的線程數是大於corePoolSize但小於maximumPoolSize,而且當任務隊列已經滿了時,則會創建新線程。通過設置corePoolSize等於maximumPoolSize,便可以創建固定大小的線程池數量。而設置maximumPoolSize爲一個不受限制的數量如Integer.MAX,便可以創建一個適應任意數量大的併發任務的線程池。常規情況下,可以根據構造方法來設置corePoolSize與maximumPoolSize,但也可以通過setCorePoolSize和setMaximumPoolSize方法動態的修改其值。
阻塞隊列

ArrayBlockingQueue :一個由數組結構組成的有界阻塞隊列。
LinkedBlockingQueue :一個由鏈表結構組成的有界阻塞隊列。
PriorityBlockingQueue :一個支持優先級排序的無界阻塞隊列。
DelayQueue: 一個使用優先級隊列實現的無界阻塞隊列。
SynchronousQueue: 一個不存儲元素的阻塞隊列。
LinkedTransferQueue: 一個由鏈表結構組成的無界阻塞隊列。

LinkedBlockingDeque: 一個由鏈表結構組成的雙向阻塞隊列。

明確拒絕策略

ThreadPoolExecutor.AbortPolicy: 丟棄任務並拋出RejectedExecutionException異常。 (默認)
ThreadPoolExecutor.DiscardPolicy:也是丟棄任務,但是不拋出異常。
ThreadPoolExecutor.DiscardOldestPolicy:丟棄隊列最前面的任務,然後重新嘗試執行任務(重複此過程)
ThreadPoolExecutor.CallerRunsPolicy:由調用線程處理該任務
說明:Executors 各個方法的弊端:
1)newFixedThreadPool 和 newSingleThreadExecutor:
主要問題是堆積的請求處理隊列可能會耗費非常大的內存,甚至 OOM。
2)newCachedThreadPool 和 newScheduledThreadPool:
主要問題是線程數最大數是 Integer.MAX_VALUE,可能會創建數量非常多的線程,甚至 OOM。

    

二、Executors工具類

最近阿里發佈的 Java開發手冊中強制線程池不允許使用 Executors 去創建,而是通過 ThreadPoolExecutor 的方式,這樣的處理方式讓寫的同學更加明確線程池的運行規則,規避資源耗盡的風險。主要是這個工具類提供的工廠方法,使用的都是默認參數,雖然可以通過一些手段進行修改,但是對於新手來說,還是存在風險。

Executors利用工廠模式向我們提供了4種線程池實現方式:

// 創建固定大小的線程池。每次提交一個任務就創建一個線程,直到線程達到線程池的最大大小。
// 線程池的大小一旦達到最大值就會保持不變,如果某個線程因爲執行異常而結束,那麼線程池會補充一個新線程。
 public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
    }
 
//創建一個單線程的線程池。這個線程池只有一個線程在工作,也就是相當於單線程串行執行所有任務。
//如果這個唯一的線程因爲異常結束,那麼會有一個新的線程來替代它。
//此線程池保證所有任務的執行順序按照任務的提交順序執行。
public static ExecutorService newSingleThreadExecutor() {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>()));
    } 

// 創建一個可緩存的線程池。如果線程池的大小超過了處理任務所需要的線程,
// 那麼就會回收部分空閒(60秒不執行任務)的線程,當任務數增加時,此線程池又可以智能的添加新線程來處理任務。
// 此線程池不會對線程池大小做限制,線程池大小完全依賴於操作系統(或者說JVM)能夠創建的最大線程大小。
public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());
    } 

//創建一個線程池,它會維持corePoolSize數目個線程,就算線程空閒,也不會被移除。可以週期地或者延時執行任務
//可以用來做定時任務,心跳檢測等功能
 public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
        return new ScheduledThreadPoolExecutor(corePoolSize);
    }	
	

三、線程調度原理





發佈了76 篇原創文章 · 獲贊 46 · 訪問量 15萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章