Executor是不建議的
Executors類為我們提供了各種類型的線程池,經常使用的工廠方法有:
public static ExecutorService newSingleThreadExecutor()
public static ExecutorService newFixedThreadPool(int nThreads)
public static ExecutorService newCachedThreadPool()
public static ScheduledExecutorService newSingleThreadScheduledExecutor()
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize)
書寫一段很簡單的測試代碼:
public class ThreadDemo {
public static void main(String[] args) {
ExecutorService es = Executors.newSingleThreadExecutor();
}
}
當我們用阿裡巴巴的P3C檢查代碼時,會被教育的!!!!
阿裡爸爸是不允許這麼建立線程池的,上面的警告寫的很明确“線程池不允許使用Executors去建立,而是通過ThreadPoolExecutor的方式,這樣的處理方式讓寫的同學更加明确線程池的運作規則,規避資源耗盡的風險。”(PS:很難得在編譯器中看到中文提示,對于英語不好的同學來說,簡直是福音,喜極而泣!!!!)
強制使用ThreadPoolExecutor
我們使用ThreadPoolExecutor建立線程池:
public class ThreadDemo {
public static void main(String[] args) {
ExecutorService es = new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>(10), Executors.defaultThreadFactory(),
new ThreadPoolExecutor.DiscardPolicy());
}
}
此時,再用P3C檢查代碼,終于沒有報錯了。
在華麗的分隔符之後,我們還是有必要從JDK源碼的層面深挖一下其中的原理。
首先是靜态方法
newSingleThreadExecutor()
、
newFixedThreadPool(int nThreads)
、
newCachedThreadPool()
。我們來看一下其源碼實作(基于JDK8)。
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
通過檢視源碼我們知道上述三種靜态方法的内部實作均使用了
ThreadPoolExecutor
類。難怪阿裡爸爸會建議通過
ThreadPoolExecutor
的方式實作,原來Executors類的靜态方法也是用的它,隻不過幫我們配了一些參數而已。
第二是
ThreadPoolExecutor
類的構造方法。既然現在要直接使用
ThreadPoolExecutor
類了,那麼其中的初始化參數就要我們自己配了,了解其構造方法勢在必行。
ThreadPoolExecutor
類一共有四個構造方法,我們隻需要了解之中的一個就可以了,因為其他三種構造方法隻是幫我們配置了一些預設參數,最後還是調用了它。
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)
其中的參數含義是:
-
:線程池中的線程數量;corePoolSize
-
:線程池中的最大線程數量;maximumPoolSize
-
:當線程池線程數量超過corePoolSize時,多餘的空閑線程會在多長時間内被銷毀;keepAliveTime
-
:keepAliveTime的時間機關;unit
-
:任務隊列,被送出但是尚未被執行的任務;workQueue
-
:線程工廠,用于建立線程,一般情況下使用預設的,即Executors類的靜态方法defaultThreadFactory();threadFactory
:拒絕政策。當任務太多來不及處理時,如何拒絕任務。handler
對于這些參數要有以下了解:
corePoolSize與maximumPoolSize的關系
首先corePoolSize肯定是 <= maximumPoolSize。
其他關系如下:
- 若目前線程池中線程數 < corePoolSize,則每來一個任務就建立一個線程去執行;
- 若目前線程池中線程數 >= corePoolSize,會嘗試将任務添加到任務隊列。如果添加成功,則任務會等待空閑線程将其取出并執行;
- 若隊列已滿,且目前線程池中線程數 < maximumPoolSize,建立新的線程;
- 若目前線程池中線程數 >= maximumPoolSize,則會采用拒絕政策(JDK提供了四種,下面會介紹到)。
注意:關系3是針對的有界隊列,無界隊列永遠都不會滿,是以隻有前2種關系。
workQueue
參數workQueue是指送出但未執行的任務隊列。若目前線程池中線程數>=corePoolSize時,就會嘗試将任務添加到任務隊列中。主要有以下幾種:
-
:直接送出隊列。SynchronousQueue沒有容量,是以實際上送出的任務不會被添加到任務隊列,總是将新任務送出給線程執行,如果沒有空閑的線程,則嘗試建立新的線程,如果線程數量已經達到最大值(maximumPoolSize),則執行拒絕政策。SynchronousQueue
-
:無界的任務隊列。當有新的任務來到時,若系統的線程數小于corePoolSize,線程池會建立新的線程執行任務;當系統的線程數量等于corePoolSize後,因為是無界的任務隊列,總是能成功将任務添加到任務隊列中,是以線程數量不再增加。若任務建立的速度遠大于任務處理的速度,無界隊列會快速增長,直到記憶體耗盡。LinkedBlockingQueue
handler
JDK内置了四種拒絕政策:
-
:丢棄任務隊列中最早添加的任務,并嘗試送出目前任務;DiscardOldestPolicy政策
-
:調用主線程執行被拒絕的任務,這提供了一種簡單的回報控制機制,将降低新任務的送出速度。CallerRunsPolicy政策
-
:默默丢棄無法處理的任務,不予任何處理。DiscardPolicy政策
-
:直接抛出異常,阻止系統正常工作。AbortPolicy政策