JDK併發包中的線程池(二)核心線程池的內部實現

對於核心的幾個線程池

newSingleThreadExecutor
newFixedThreadPool
newCachedThreadPool

其內部都是使用的ThreadPoolExecutor實現的,下面給出它們的實現方式:

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>()));
}

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

我們再來看看ThreadPoolExecutor的構造方法

public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler) {
}

參數說明:
corePoolSize:線程池中線程的數量
maximumPoolSize:線程池中最大的線程的數量
keepAliveTime:當線程池中線程的數量超過maximumPoolSize時,多餘的空閒的線程的存活時間
unit:keepAliveTime的單位
workQueue:任務隊列,存放已經提交但尚未被執行的任務
threadFactory:產生現成的工廠
handler:當任務太多來不及執行的時候的拒絕策略
重點介紹workQueue,它是一個BlockingQueue的接口的對象,根據隊列的功能分類,可以使用下面幾種BlockingQueue的實現:
1. SynchronousQueue,其沒有容量,沒進行一次put都必須等待一個相應的take,反之亦然,提交的任務不會被真實的保存,而總是將新任務提交給線程執行,如果沒有空閒的線程並且線程數量還沒有達到maximumPoolSize,則嘗試創建新的線程,如果已經達到最大的線程數,那麼就會執行拒絕策略
2. ArrayBlockingQueue,ArrayBlockingQueue必須包含一個參數,用於表示該隊列的最大的容量,如果線程池的數量小於corePoolSize,那麼提交任務後就會創建新的線程用於執行任務,如果線程池的數量大於corePoolSize,那麼就會將任務假如等待隊列ArrayBlockingQueue中,如果隊列滿了的話,線程池中線程的數量也沒有達到maximumPoolSize,那麼會創建新的線程執行任務,若大於了maxmium,那麼就會執行拒絕策略。由此可見,ArrayBlockingQueue只有在隊列滿了的情況下,線程池中的線程數量纔會突破corePoolSize個。
3. LinkedBlockingQueue,與有界隊列ArrayBlockingQueue不一樣的,除非系統資源耗盡,否則無界隊列不存在任務入隊失敗的情況,當有新的任務進來的時候,如果線程池中線程的數量不足corePoolSize,那麼會創建新的線程執行任務,但是當線程池中的線程的數量達到corePoolSize的時候,則任務就會直接進入到LinkedBlockingQueue中

在此,我們就不難發現爲什麼newFixedThreadPool實現的時候使用的是LinkedBlockingQueue,而newCachedThreadPool實現的時候使用的是SynchronousQueue。

拒絕策略
JDK內置的拒絕策略如下:
AbortPolicy:直接拋出異常
CallerRunsPolicy:直接在調用者的線程中,運行當前被丟棄的任務
DiscardOledestPolicy:丟棄掉最老的一個任務
DiscardPolicy:默默地丟棄無法處理的任務,不予任何處理
拒絕策略的源代碼如下所示:

public static class AbortPolicy implements RejectedExecutionHandler {    
        public AbortPolicy() { } 
        public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
            throw new RejectedExecutionException("Task " + r.toString() +
                                                 " rejected from " +
                                                 e.toString());
        }
}

public static class CallerRunsPolicy implements RejectedExecutionHandler {
        public CallerRunsPolicy() { }
        public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
            if (!e.isShutdown()) {
                r.run();
            }
        }
}

public static class DiscardOldestPolicy implements RejectedExecutionHandler {
        public DiscardOldestPolicy() { }
        public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
            if (!e.isShutdown()) {
                e.getQueue().poll();
                e.execute(r);
            }
        }
}

public static class DiscardPolicy implements RejectedExecutionHandler {
        public DiscardPolicy() { }
        public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
        }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章