線程池的創建
1.Executors直接創建:
Executors.newCachedThreadPool(); 創建無界的線程池,自動線程回收,
Executors.newFixedThreadPool(n); 創建固定大小線程池
Executors.new singleThreadExecutor(); 創建單一後臺線程
Executors.newScheduleThreadPool(); 創建執行計劃的線程
2.通過ThreadPoolExecutor創建: 下文介紹。
3.建議:
在阿里規範中:線程池不允許使用Executors來創建,而是通過ThreadPoolExecutor來創建, 這樣更能明確線
程池的運行規則,規避資源耗盡的風險,
Executors的各個弊端:
1).newFixedThreadPool和newSingleThreadExecutor:
主要問題是堆積的請求處理隊列可能會耗費非常大的內存,甚至OOM(內存溢出)。
2).newCachedThreadPool和newScheduledThreadPool:
主要問題是線程數最大數是Integer.MAX_VALUE,可能會創建數量非常多的線程,甚至OOM。
線程池的關閉:
1.shutdown(): 調用該方法,不會馬上終止線程池,而是等所有緩存隊列中的任務都執行完才終止,但再也不會接受新的任務。
2.shutdownNow(): 立即終止線程池,並嘗試打斷正在執行的任務,並且清空任務緩存隊列,返回尚未執行的任務。
ThreadPoolExecutor:
1.構造方法:
2.構造方法參數:
corePoolSize | int | 核心線程池大小 |
---|---|---|
maximumPoolSize | int | 最大線程池大小 |
keepAliveTime | long | 線程最大空閒時間 |
unit | TimeUnit | 線程最大空閒時間單位 |
workQueue | BlockingQueue< Runnable > | 線程阻塞隊列 |
threadFactory | ThreadFactory | 線程創建工廠 |
handler | RejectedExecutionHandler | 拒絕策略 |
3.一般創建線程池方式:
4.參數含義:
1).corePoolSize: 表示改線程池中核心線程的大小。
線程池創建之後,線程池中的線程數爲0
當任務過來就創建一個核心線程去執行,直到線程數達到corePoolSize 之後,接受的任務放在阻塞隊列中。
核心線程:線程池中有兩類線程,核心線程和非核心線程。核心線程默認情況下會一直存在於線程池中,即使這個核心線程什麼都不幹(鐵飯碗),而非核心線程如果長時間的閒置,就會被銷燬(臨時工)
2).maximumPoolSize: 最大線程池大小。
線程池允許的最大線程數,該值等於核心線程數量 + 非核心線程數量。
maximumPoolSize肯定是大於等於corePoolSize。
通過設置corePoolSize和maximumPoolSize相同,可以創建一個FixedThreadPool;
3).keepAliveTime: 線程最大空閒時間。
如果線程池當前擁有超過corePoolSize的線程,多餘線程(非核心線程)超過keepAliveTime時就會被終止 。
如果設置allowCoreThreadTimeOut(true),則會也作用於核心線程。
默認情況下,只有線程池中線程數大於corePoolSize 時,keepAliveTime 纔會起作用。
4).unit: keepAliveTime 的單位。
5).workQueue: 線程阻塞隊列。
當線程池中的線程數超過它的corePoolSize的時候,線程會進入阻塞隊列進行阻塞等待。
workQueue的類型爲BlockingQueue< Runnable >,通常可以取下面三種類型:
a) ArrayBlockingQueue:
有界任務隊列,基於數組的先進先出隊列,此隊列創建時必須指定大小;
b) LinkedBlockingQueue:
無界任務隊列,基於鏈表的先進先出隊列
如果創建時沒有指定此隊列大小,則默認爲Integer.MAX_VALUE;
c) synchronousQueue:
直接提交隊列,這個隊列比較特殊,它不會保存提交的任務,而是將直接新建一個線程來執行新來的任務。
6).threadFactory : 線程工廠,用來創建線程。
Executors的線程池如果不指定線程工廠會使用Executors中的DefaultThreadFactory,
默認線程池工廠創建的線程都是非守護線程。
7).handler: 線程池的拒絕策略.
當線程池的任務緩存隊列已滿並且線程池中的線程數目達到maximumPoolSize
如果還有任務到來就會採取任務拒絕策略,通常有以下四種策略:
a). AbortPolicy:
線程池默認會採用的是defaultHandler策略,即:
private static final RejectedExecutionHandler defaultHandler = new AbortPolicy();
該策略會丟棄任務 並拋出RejectedExecutionException異常。
b). DiscardPolicy:
也是丟棄任務,但是不拋出異常。
c).DiscardOldestPolicy:
線程池會丟棄阻塞隊列中末尾的任務 ,然後將被拒絕的任務添加到末尾。
e).CallerRunsPolicy:
當有任務添加到線程池被拒絕時,線程池會將被拒絕的任務添加到”線程池正在運行的線程”中取運行。
線程池正在運行的線程:比如在main方法開啓線程池,執行拒絕策略時,main的線程就是正在運行的線程
線程池的任務處理策略:
1.當前線程池中的線程數目小於corePoolSize:
則每來一個任務,就會創建一個線程去執行這個任務;
2.當前線程池中的線程數目>=corePoolSize
則每來一個任務,會嘗試將其添加到任務緩存隊列當中
若添加成功,則該任務會等待空閒的核心線程將其取出去執行;
若添加失敗(一般來說是任務緩存隊列已滿),則會嘗試創建新的線程(非核心線程)去執行這個任務;
如果當前線程池中的線程數目達到maximumPoolSize,則會採取任務拒絕策略進行處理;
線程池原理:
1.線程池狀態:
ThreadPoolExecutor類線程池的狀態 ,分別爲RUNNING、SHURDOWN、STOP、TIDYING、TERMINATED。
線程池創建後處於RUNNING狀態。
調用shutdown()方法後處於SHUTDOWN狀態
調用shutdownNow()方法後處於STOP狀態
當所有的任務已終止,線程池會變爲TIDYING狀態。接着會執行terminated()函數。
線程池處在TIDYING狀態時,執行完terminated()方法之後, 線程池被設置爲TERMINATED狀態。
2.線程池核心的execute()方法: 處理任務的核心方法是execute
ctl.get()是獲取線程池狀態,用int類型表示。
爲什麼要二次檢查線程池的狀態?
第二步,入隊前進行了一次isRunning判斷,入隊後,又進行了一次isRunning判斷。
倘若沒有二次檢查,萬一線程池處於非RUNNING狀態(在多線程環境下很有可能發生),那麼任務永遠不會執行
3.線程複用:
一個線程在創建的時候會指定一個線程任務,當執行完這個線程任務之後,線程自動銷燬。
但是線程池卻可以複用線程,即一個線程執行完線程任務後不銷燬,繼續執行另外的線程任務。
在addWorker方法中,會將線程任務firstTask封裝到工作線程worker裏
然後再將工作線程add進工作線程組workers裏
當t.start();啓動線程後,會觸發Worker類的run方法調用runWoker()方法。
在while循環中,worker會不斷地調用getTask()方法從阻塞隊列中獲取任務。
只要getTask方法不返回null,此線程就不會退出。
然後調用task.run()執行任務,從而達到複用線程的目的。