Java併發——Executor框架ThreadPoolExecutor詳解

ThreadPoolExecutor是Executor接口的一個重要的實現類,是線程池的具體實現,用來執行被提交的任務。

一、ThreadPoolExecutor的創建:

  • 直接創建ThreadPoolExecutor的實例對象,這樣需要自己配置ThreadPoolExecutor中的每一個參數:
ThreadPoolExecutor tpe = new ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit, BlockingQueue<Runnable> workQueue);
每個參數的具體設置參見:點擊打開鏈接
  • ThreadPoolExecutor通過Executors工具類來創建ThreadPoolExecutor的子類FixedThreadPool、SingleThreadExecutor、CachedThreadPool,這些子類繼承ThreadPoolExecutor,並且其中的一些參數已經被配置好。
//FixedThreadPoll
ExecutorService ftp = Executors.newFixedThreadPool(int threadNums);
ExecutorService ftp = Executors.newFixedThreadPool(int threadNums, ThreadFactory threadFactory);
//SingleThreadExecutor
ExecutorService ste = Executors.newSingleThreadExecutor();
ExecutorService ste = Executors.newSingleThradPool(ThreadFactory threadFactory);
//CachedThreadPool
ExecutorService ctp = Executors.newCachedThreadPool();
ExecutorService ctp = Executors.newCachedThreadPool(ThreadFactory threadFactory);

關於ThreadFactory接口,參見:點擊打開鏈接


二、ThreadPoolExecutor的子類FixedThreadPool、SingleThreadExecutor、CachedThreadPool詳解

FixedThreadPool、SingleThreadExecutor、CachedThreadPool都是通過Executors工廠類中的工廠方法創建的,因此我們對這幾個方法進行分析。

1、FixedThreadPool

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

應用場景:FixedThreadPool是線程數量固定的線程池,適用於爲了滿足資源管理的需求,而需要適當限制當前線程數量的情景,適用於負載比較重的服務器。

可以看出它的實現就是把線程池最大線程數量maxmumPoolSize和核心線程池的數量corePoolSize設置爲相等,並且使用LinkedBlockingQueue作爲阻塞隊列,那麼首先可以知道線程池的線程數量最多就是nThread,只會在核心線程池階段創建,此外,因爲LinkedBlockingQueue是無限的雙向隊列,因此當任務不能立刻執行時,都會添加到阻塞隊列中,因此可以得到FixedThreadPool的工作流程大致如下:

  • 當前核心線程池總線程數量小於corePoolSize,那麼創建線程並執行任務;
  • 如果當前線程數量等於corePoolSize,那麼把 任務添加到阻塞隊列中;
  • 如果線程池中的線程執行完任務,那麼獲取阻塞隊列中的任務並執行;

*注意:因爲阻塞隊列是無限的雙向隊列,因此如果沒有調用shutDownNow()或者shutDown()方法,線程池是不會拒絕任務的,如果線程池中的線程一直被佔有,FixedThreadPool是不會拒絕任務的。

因爲使用的是LinkedBlockingQueue,因此maximumPoolSize,keepAliveTime都是無效的,因爲阻塞隊列是無限的,因此線程數量肯定小於等於corePoolSize,因此keepAliveTime是無效的;


2、SingleThreadExecutor

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

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

應用場景:SingleThreadExecutor是隻有一個線程的線程池,常用於需要讓線程順序執行,並且在任意時間,只能有一個任務被執行,而不能有多個線程同時執行的場景。

因爲阻塞隊列使用的是LinkedBlockingQueue,因此和FixedThreadPool一樣,maximumPoolSize,keepAliveTime都是無效的。corePoolSize爲1,因此最多隻能創建一個線程,SingleThreadPool的工作流程大概如下:

  • 當前核心線程池總線程數量小於corePoolSize(1),那麼創建線程並執行任務;
  • 如果當前線程數量等於corePoolSize,那麼把 任務添加到阻塞隊列中;
  • 如果線程池中的線程執行完任務,那麼獲取阻塞隊列中的任務並執行;


3、CachedThreadPool

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

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

應用場景:CachedThreadPool適用於執行很多短期異步任務的小程序,或者是負載較輕的服務器。

CachedThreadPool使用SynchronizedQueue作爲阻塞隊列,SynchronizedQueue是不存儲元素的阻塞隊列,實現“一對一的交付”,也就是說,每次向隊列中put一個任務必須等有線程來take這個任務,否則就會一直阻塞該任務,如果一個線程要take一個任務就要一直阻塞知道有任務被put進阻塞隊列。

因爲CachedThreadPool的maximumPoolSize爲Integer.MUX_VALUE,因此CachedThreadPool是無界的線程池,也就是說可以一直不斷的創建線程。corePoolSize爲0 ,因此在CachedThreadPool中直接通過阻塞隊列來進行任務的提交。

CachedThreadPool的工作流程大概如下:

  • 首先執行SynchronizedQueue.offer(  )把任務提交給阻塞隊列,如果這時候正好在線程池中有空閒的線程執行SynchronizedQueue.poll( ),那麼offer操作和poll操作配對,線程執行任務;
  • 如果執行SynchronizedQueue.offer(  )把任務提交給阻塞隊列時maximumPoolSize=0.或者沒有空閒線程來執行SynchronizedQueue.poll( ),那麼步驟1失敗,那麼創建一個新線程來執行任務;
  • 如果當前線程執行完任務則循環從阻塞隊列中獲取任務,如果當前隊列中沒有提交(offer)任務,那麼線程等待keepAliveTime時間,在CacheThreadPool中爲60秒,在keepAliveTime時間內如果有任務提交則獲取並執行任務,如果沒有則銷燬線程,因此最後如果一直沒有任務提交了,線程池中的線程數量最終爲0。
*注意:因爲maximumPoolSize=Integer.MAX_VALUE,因此可以不斷的創建新線程,這樣可能會CPU和內存資源耗盡。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章