之前學習Java編程思想,一直喜歡使用Executors來創建線程。Executors有以下四個方法創建線程:
- Executors.newFixedThreadPool() 創建一個裝有固定數量線程的線程池。
- Executors.newSingleThreadExecutor() 單線程的線程池。
- Executors.newCachedThreadPool() 讓線程池根據處理需求動態地調整線程數量,比如線程池內線程的數量超過需求時,自動回收線程。
- Executors.newScheduledThreadPool() 創建一個定時、週期性執行任務的線程池。
但是,阿里巴巴Java開發手冊關於線程池的用法中,明確指出:
不允許使用Executors來創建線程池
【強制】線程池不允許使用Executors去創建,而是通過ThreadPoolExecutor的方式,這樣的處理方式讓寫的同學更加明確線程池的運行規則,避免資源耗盡的風險。
說明: Executors返回的線程池對象的弊端如下:
- FixedThreadPool和SingleThreadPool允許的請求隊列長度爲Integer.MAX_VALUE,可能會堆積大量的請求,從而導致OOM。
- 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;
}
}