為什麼阿里巴巴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手動地去創建執行緒池,執行緒池大小應該有個邊界,並選取合適的隊列存儲任務。