四種常見線程池的原理,你學會了嗎?
newFixedThreadPool (固定數目線程的線程池)
newCachedThreadPool (可緩存線程的線程池)
newSingleThreadExecutor (單線程的線程池)
newScheduledThreadPool (定時及周期執行的線程池)
前三種線程池的構造直接調用ThreadPoolExecutor的構造方法。
newSingleThreadExecutor
public static ExecutorService newSingleThreadExecutor(ThreadFactory threadFactory) {
return new FinalizableDelegatedExecutorService(new ThreadPoolExecutor(1, 1,0L,TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>(),threadFactory));
}
線程池特點:
核心線程數:1
最大線程數:1
阻塞隊列是無界隊列LinkedBlockingQueue,可能會導致OOM
keepAliveTime:0
工作流程:
1、提交任務
2、線程池是否有一條線程在,如果沒有,新建線程執行任務
3、如果有,將任務加到阻塞隊列
4、當前的唯一線程,從隊列取任務,執行完一個,再繼續,一個線程執行任務
適用場景:
適用于串行執行任務的場景,一個任務一個任務地執行
newFixedThreadPool
public static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory) {
return new ThreadPoolExecutor(nThreads, nThreads,0L,TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>(), threadFactory);
}
線程池特點:
核心線程數和最大線程數大小一樣;
沒有所謂的非空閑時間,即keepAliveTime為0;
阻塞隊列為無界隊列LinkedBlockingQueue,可能會導致OOM;
工作流程:
1、提交任務;
2、如果線程數少于核心線程,創建核心線程執行任務;
3、如果線程數等于核心線程,把任務添加到LinkedBlockingQueue阻塞隊列;
4、如果線程執行完任務,去阻塞隊列取任務,繼續執行。
適用場景:
FixedThreadPool適用于處理CPU密集型的任務,確保CPU在長期被工作線程使用的情況下,盡可能少的分配線程,即適用執行長期的任務。
newCachedThreadPool
public static ExecutorService newCachedThreadPool(ThreadFactory threadFactory) {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>(), threadFactory);
}
線程池特點:
核心線程數為0;
最大線程數為Integer.MAX_VALUE,即無限大,可能會因為無線創建線程,導致OOM
阻塞隊列是SynchronousQueue
非核心線程空閑存活時間為60s
當提交任務速度大于處理任務的速度時,每次提交一個任務,就必然會創建一個線程。
極端情況下會創建過多的線程,耗盡CPU和內存資源。由于空閑60秒的線程會被終止,長時間保持空閑的CachedThreadPool不會占用任何資源。
工作流程:
提交任務;
因為沒有核心線程,所以任務會直接加到SynchronousQueue隊列;
判斷是否有空閑線程,如果有,就去取出任務執行;
如果沒有空閑線程,就新建一個線程執行;
執行完任務的線程,還可以存活60s,如果在這期間接到任務,就可以繼續活下去,否則被銷毀。
適用場景:
用于并發執行大量短期的小任務。
newScheduleThreadPool
public ScheduledThreadPoolExecutor(int corePoolSize) {
super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS, new DelayedWorkQueue());
}
線程池特點:
最大線程數為Integer.MAX_VALUE,也有OOM風險
阻塞隊列是DelayedWorkQueue
keepAliveTime為0
scheduleAtFixedRate():按某種速率周期執行
scheduleWithFixedDelay():在某個延遲后執行
工作機制:
線程從DelayQueue中獲取已到期的ScheduledFutureTask(DelayQueue.take())。到期任務是指ScheduledFutureTask的time大于等于當前時間。
線程執行這個ScheduleFutureTask
線程修改ScheduledFutureTask的time變量為下次將要被執行的時間
線程把這個修改time之后的ScheduledFutureTask放回DelayQueue中(DelayQueue.add())。
使用場景:
周期性執行任務的場景,需要限制線程數量的場景。
使用無界隊列的線程池會導致什么問題:
例如newFixedThreadPool使用了無界的阻塞隊列LinkedBlockingQueue,如果線程獲取一個任務后,任務的執行時間比較長,會導致隊列的任務越積越多,導致機器內存使用不停飆升,最終導致OOM。