阿里巴巴禁止使用Executors創建線程池的原因

之前學習Java編程思想,一直喜歡使用Executors來創建線程。Executors有以下四個方法創建線程:

  1. Executors.newFixedThreadPool() 創建一個裝有固定數量線程的線程池。
  2. Executors.newSingleThreadExecutor() 單線程的線程池。
  3. Executors.newCachedThreadPool() 讓線程池根據處理需求動態地調整線程數量,比如線程池內線程的數量超過需求時,自動回收線程。
  4. Executors.newScheduledThreadPool() 創建一個定時、週期性執行任務的線程池。

但是,阿里巴巴Java開發手冊關於線程池的用法中,明確指出:
不允許使用Executors來創建線程池

【強制】線程池不允許使用Executors去創建,而是通過ThreadPoolExecutor的方式,這樣的處理方式讓寫的同學更加明確線程池的運行規則,避免資源耗盡的風險。
說明: Executors返回的線程池對象的弊端如下:

  1. FixedThreadPool和SingleThreadPool允許的請求隊列長度爲Integer.MAX_VALUE,可能會堆積大量的請求,從而導致OOM。
  2. CachedThreadPool和ScheduleThreadPool允許創建的線程數量爲Integer.MAX_VALUE,可能會產生大量的線程,從而導致OOM。

源碼分析:

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 ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue) {
        this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
             Executors.defaultThreadFactory(), defaultHandler);
}

可以發現,在初始化LinkedBlockingQueue時,沒有指定容量,同時也沒有任何針對請求數量限制的參數,此時LinkedBlockingQueue就是一個無邊界隊列,因此可以無限制的向這個隊列中加入請求,可能因爲隊列中的請求過多導致內存溢出。

注意: LinkedBlockingQueue一旦容量被設定,如果存儲的值已達上限,隊列不會自動擴容,而是使當前線程進入等待狀態,請看代碼:

/** Wait queue for waiting puts */
private final Condition notFull = putLock.newCondition();

public void put(E e) throws InterrupredException{
	...
	 while (count.get() == capacity) {
                notFull.await();
     }
	...
}

阿里巴巴推薦的線程池創建方式
方法1: commons-lang3
使用commons-lang3提供的org.apache.commons.lang3.concurrent.BasicThreadFactory作爲線程池初始化線程的線程工廠。

BasicThreadFactory threadFactory = new BasicThreadFactory.Builder()
                .namingPattern("example-schedule-pool-%d").daemon(true).build();
ScheduledThreadPoolExecutor exec = new ScheduledThreadPoolExecutor(1, threadFactory);

方法2: com.google.guava

ThreadFactory threadFactory = new ThreadFactoryBuilder().setNameFormat("example-thread-pool-%d").build();

ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(5, 200, 0L, TimeUnit.MILLISECONDS,
                new LinkedBlockingQueue<>(1024), threadFactory, new ThreadPoolExecutor.AbortPolicy());

方法3: 自定義ThreadFactory

public class Test {
    public static void main(String[] args) throws InterruptedException {
        ExecutorService exec = Executors.newCachedThreadPool(new MyThreadPoolFactory(Thread.MAX_PRIORITY));
    }

}

class MyThreadPoolFactory implements ThreadFactory{
    private final int priority;

    MyThreadPoolFactory(int priority) {
        this.priority = priority;
    }

    @Override
    public Thread newThread(Runnable r) {
        Thread t = new Thread(r);
        t.setName("線程的名字");
        t.setPriority(priority);
        return t;
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章