1.什麼是線程
線程池就是提前創建若干個線程,如果有任務需要處理,線程池裏的線程就會處理任務,處理完之後線程並不會被銷燬,而是等待下一個任務。由於創建和銷燬線程都是消耗系統資源的,所以當你想要頻繁的創建和銷燬線程的時候就可以考慮使用線程池來提升系統的性能。
java.util.concurrent.Executors提供了一個 java.util.concurrent.Executor接口的實現用於創建線程池
2.括以下四個基本組成部分:
①線程池管理器(ThreadPool):用於創建並管理線程池,包括 創建線程池,銷燬線程池,添加新任務;
②工作線程(PoolWorker):線程池中線程,在沒有任務時處於等待狀態,可以循環的執行任務;
③任務接口(Task):每個任務必須實現的接口,以供工作線程調度任務的執行,它主要規定了任務的入口,任務執行完後的收尾工作,任務的執行狀態等;
④任務隊列(taskQueue):用於存放沒有處理的任務。提供一種緩衝機制。
3.線程池有什麼作用?
①提高效率 創建好一定數量的線程放在池中,等需要使用的時候就從池中拿一個,比需要的時候創建一個線程對象要快的多。
②方便管理 可以編寫線程池管理代碼對池中的線程同一進行管理,比如說啓動時有該程序創建100個線程,每當有請求的時候,就分配一個線程去工作,如果剛好併發有101個請求,那多出的這一個請求可以排隊等候,避免因無休止的創建線程導致系統崩潰。
4.常見線程池:
①newSingleThreadExecutor
單個線程的線程池,即線程池中每次只有一個線程工作,單線程串行執行任務
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
②newFixedThreadExecutor(n)
固定數量的線程池,沒提交一個任務就是一個線程,直到達到線程池的最大數量,然後後面進入等待隊列直到前面的任務完成才繼續執行
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
③newCacheThreadExecutor(推薦使用)
可緩存線程池,當線程池大小超過了處理任務所需的線程,那麼就會回收部分空閒(一般是60秒無執行)的線程,當有任務來時,又智能的添加新線程來執行。
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
④newScheduleThreadExecutor
大小無限制的線程池,支持定時和週期性的執行線程
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
return new ScheduledThreadPoolExecutor(corePoolSize);
}
5.我們看一下線程池的類庫:
6.線程池常見參數:
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) {
if (corePoolSize < 0 ||
maximumPoolSize <= 0 ||
maximumPoolSize < corePoolSize ||
keepAliveTime < 0)
throw new IllegalArgumentException();
if (workQueue == null || threadFactory == null || handler == null)
throw new NullPointerException();
this.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.workQueue = workQueue;
this.keepAliveTime = unit.toNanos(keepAliveTime);
this.threadFactory = threadFactory;
this.handler = handler;
}
corePoolSize:核心線程數量,會一直存在,除非allowCoreThreadTimeOut設置爲true
maximumPoolSize:線程池允許的最大線程池數量
keepAliveTime:線程數量超過corePoolSize,空閒線程的最大超時時間
unit:超時時間的單位
workQueue:工作隊列,保存未執行的Runnable 任務
threadFactory:創建線程的工廠類
handler:當線程已滿,工作隊列也滿了的時候,會被調用。被用來實現各種拒絕策略。
7.有界隊列和無界隊列
有界隊列
1.初始的poolSize 小於 corePoolSize,提交的runnable任務,會直接做爲new一個Thread的參數,立馬執行 。
2.當提交的任務數超過了corePoolSize,會將當前的runable提交到一個block queue中。
3.有界隊列滿了之後,如果poolSize 小於 maximumPoolsize時,會嘗試new 一個Thread的進行救急處理,立馬執行對應的runnable任務。
4.如果3中也無法處理了,就會走到第四步執行reject操作。
無界隊列
與有界隊列相比,除非系統資源耗盡,否則無界的任務隊列不存在任務入隊失敗的情況。當有新的任務到來,系統的線程數小於corePoolSize時,則新建線程執行任務。當達到corePoolSize後,就不會繼續增加,若後續仍有新的任務加入,而沒有空閒的線程資源,則任務直接進入隊列等待。若任務創建和處理的速度差異很大,無界隊列會保持快速增長,直到耗盡系統內存。
當線程池的任務緩存隊列已滿並且線程池中的線程數目達到maximumPoolSize,如果還有任務到來就會採取任務拒絕策略。
8.如何合理分配線程池的大小:
一般說來,大家認爲線程池的大小經驗值應該這樣設置:(其中N爲CPU的個數)
- 如果是CPU密集型應用,則線程池大小設置爲N+1
- 如果是IO密集型應用,則線程池大小設置爲2N+1