為什麼阿里巴巴Java開發手冊中不允許用Executors去創建執行緒池?

  • 2019 年 10 月 7 日
  • 筆記

在我閱讀阿里巴巴開發手冊的時候,有一段關於多執行緒的描述:

執行緒池不允許使用 Executors 去創建,而是通過 ThreadPoolExecutor 的方式,這樣 的處理方式讓寫的同學更加明確執行緒池的運行規則,規避資源耗盡的風險。 說明: Executors 返回的執行緒池對象的弊端如下: FixedThreadPool 和 SingleThreadPool : 允許的請求隊列長度為 Integer.MAX_VALUE ,可能會堆積大量的請求,從而導致 OOM 。 CachedThreadPool 和 ScheduledThreadPool : 允許的創建執行緒數量為 Integer.MAX_VALUE ,可能會創建大量的執行緒,從而導致 OOM 。

當看到不允許使用Executors創建執行緒池的時候,我有點懵,仔細一看不無道理。

我們來逐個分析。

FixedThreadPool 和 SingleThreadPool

這兩個執行緒池是執行緒池大小是固定的。SingleThreadPool是單個執行緒的執行緒池。FixedThreadPool在應對平穩流量的時候,能有效的處理,缺點就是可能無法應付突發性大流量。

使用Executors創建:

ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();  ExecutorService fixedThreadPool = Executors.newFixedThreadPool(10);

我們點開方法看一下:

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

兩個方法,都通過了 LinkedBlockingQueue<Runnable>來接收來不及處理的任務。關鍵點就在這個隊列里,默認的構造器容量是Integer.MAX_VALUE。

public LinkedBlockingQueue() {   this(Integer.MAX_VALUE);   }

那就是說,當流量突然變得非常大時,執行緒池滿,等候隊列變得非常龐大,記憶體和CPU都告急,這樣無疑對伺服器造成非常大的壓力。

CachedThreadPool 和 ScheduledThreadPool

ExecutorService cacheThreadPool = Executors.newCachedThreadPool();  public static ExecutorService newCachedThreadPool() {   return new ThreadPoolExecutor(0, Integer.MAX_VALUE,   60L, TimeUnit.SECONDS,   new SynchronousQueue<Runnable>());   }  ExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(10);  public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {   return new ScheduledThreadPoolExecutor(corePoolSize);   }  public ScheduledThreadPoolExecutor(int corePoolSize) {   super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,   new DelayedWorkQueue());   }

同理,從工廠方法可以看到,這兩種執行緒池,執行緒池大小是不固定的,雖然newScheduledThreadPool傳如一個執行緒數,但是這個數字只是核心執行緒數,可能還會擴容,直至Integer.MAX_VALUE。而他們使用的隊列是SynchronousQueue和DelayedWorkQueue。這兩個隊列我沒有細看,但初始化時不會像LinkedBlockingQueue那樣一下子將容量調整到最大。

總結:阿里手冊希望程式設計師們根據業務情況,通過ThreadPoolExecutor手動地去創建執行緒池,執行緒池大小應該有個邊界,並選取合適的隊列存儲任務。