阿里巴巴:禁止使用 Executors 創建線程池!爲什麼啊?

​來源:​blog.csdn.net/dabusiGin/article/details/105327873/

Executor是不建議的

Executors類爲我們提供了各種類型的線程池,經常使用的工廠方法有:

public static ExecutorService newSingleThreadExecutor()
public static ExecutorService newFixedThreadPool(int nThreads)
public static ExecutorService newCachedThreadPool()
public static ScheduledExecutorService newSingleThreadScheduledExecutor()
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize)

書寫一段很簡單的測試代碼:

public class ThreadDemo {
 public static void main(String[] args) {
  ExecutorService es = Executors.newSingleThreadExecutor();
 }
}

當我們用阿里巴巴的P3C檢查代碼時,會被教育的!!!!

阿里爸爸是不允許這麼創建線程池的,上面的警告寫的很明確“線程池不允許使用Executors去創建,而是通過ThreadPoolExecutor的方式,這樣的處理方式讓寫的同學更加明確線程池的運行規則,規避資源耗盡的風險。”(PS:很難得在編譯器中看到中文提示,對於英語不好的同學來說,簡直是福音,喜極而泣!!!!)

強制使用ThreadPoolExecutor

我們使用ThreadPoolExecutor創建線程池:

public class ThreadDemo {
 public static void main(String[] args) {
  ExecutorService es = new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS,
    new LinkedBlockingQueue<Runnable>(10), Executors.defaultThreadFactory(),
    new ThreadPoolExecutor.DiscardPolicy());
 }
}

此時,再用P3C檢查代碼,終於沒有報錯了。

在華麗的分隔符之後,我們還是有必要從JDK源碼的層面深挖一下其中的原理。

首先是靜態方法newSingleThreadExecutor()newFixedThreadPool(int nThreads)newCachedThreadPool()。我們來看一下其源碼實現(基於JDK8)。

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

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

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

通過查看源碼我們知道上述三種靜態方法的內部實現均使用了ThreadPoolExecutor類。難怪阿里爸爸會建議通過ThreadPoolExecutor的方式實現,原來Executors類的靜態方法也是用的它,只不過幫我們配了一些參數而已。

第二是ThreadPoolExecutor類的構造方法。既然現在要直接使用ThreadPoolExecutor類了,那麼其中的初始化參數就要我們自己配了,瞭解其構造方法勢在必行。

ThreadPoolExecutor類一共有四個構造方法,我們只需要瞭解之中的一個就可以了,因爲其他三種構造方法只是幫我們配置了一些默認參數,最後還是調用了它。

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

其中的參數含義是:

  • corePoolSize:線程池中的線程數量;
  • maximumPoolSize:線程池中的最大線程數量;
  • keepAliveTime:當線程池線程數量超過corePoolSize時,多餘的空閒線程會在多長時間內被銷燬;
  • unit:keepAliveTime的時間單位;
  • workQueue:任務隊列,被提交但是尚未被執行的任務;
  • threadFactory:線程工廠,用於創建線程,一般情況下使用默認的,即Executors類的靜態方法defaultThreadFactory();handler:拒絕策略。當任務太多來不及處理時,如何拒絕任務。

對於這些參數要有以下了解:

corePoolSize與maximumPoolSize的關係

首先corePoolSize肯定是 <= maximumPoolSize。

其他關係如下:

  • 若當前線程池中線程數 < corePoolSize,則每來一個任務就創建一個線程去執行;
  • 若當前線程池中線程數 >= corePoolSize,會嘗試將任務添加到任務隊列。如果添加成功,則任務會等待空閒線程將其取出並執行;
  • 若隊列已滿,且當前線程池中線程數 < maximumPoolSize,創建新的線程;
  • 若當前線程池中線程數 >= maximumPoolSize,則會採用拒絕策略(JDK提供了四種,下面會介紹到)。

注意:關係3是針對的有界隊列,無界隊列永遠都不會滿,所以只有前2種關係。

workQueue

參數workQueue是指提交但未執行的任務隊列。若當前線程池中線程數>=corePoolSize時,就會嘗試將任務添加到任務隊列中。主要有以下幾種:

  • SynchronousQueue:直接提交隊列。SynchronousQueue沒有容量,所以實際上提交的任務不會被添加到任務隊列,總是將新任務提交給線程執行,如果沒有空閒的線程,則嘗試創建新的線程,如果線程數量已經達到最大值(maximumPoolSize),則執行拒絕策略。
  • LinkedBlockingQueue:無界的任務隊列。當有新的任務來到時,若系統的線程數小於corePoolSize,線程池會創建新的線程執行任務;當系統的線程數量等於corePoolSize後,因爲是無界的任務隊列,總是能成功將任務添加到任務隊列中,所以線程數量不再增加。若任務創建的速度遠大於任務處理的速度,無界隊列會快速增長,直到內存耗盡。

handler

JDK內置了四種拒絕策略:

  • DiscardOldestPolicy策略:丟棄任務隊列中最早添加的任務,並嘗試提交當前任務;
  • CallerRunsPolicy策略:調用主線程執行被拒絕的任務,這提供了一種簡單的反饋控制機制,將降低新任務的提交速度。
  • DiscardPolicy策略:默默丟棄無法處理的任務,不予任何處理。
  • AbortPolicy策略:直接拋出異常,阻止系統正常工作。

至此,我們直接new ThreadPoolExecutor類就不用慌了!!!!

近期熱文推薦:

1.1,000+ 道 Java面試題及答案整理(2022最新版)

2.勁爆!Java 協程要來了。。。

3.Spring Boot 2.x 教程,太全了!

4.別再寫滿屏的爆爆爆炸類了,試試裝飾器模式,這纔是優雅的方式!!

5.《Java開發手冊(嵩山版)》最新發布,速速下載!

覺得不錯,別忘了隨手點贊+轉發哦!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章