在前面的文章中正压,我們使用線程的時候就去創(chuàng)建一個線程猜年,這樣實現(xiàn)起來非常簡便,但是就會有一個問題:
如果并發(fā)的線程數(shù)量很多怜珍,并且每個線程都是執(zhí)行一個時間很短的任務就結束了,這樣頻繁創(chuàng)建線程就會大大降低系統(tǒng)的效率凤粗,因為頻繁創(chuàng)建線程和銷毀線程需要時間和系統(tǒng)資源酥泛。
那么有沒有一種辦法使得線程可以復用今豆,就是執(zhí)行完一個任務,并不被銷毀柔袁,而是可以繼續(xù)執(zhí)行其他的任務呆躲?在Java中可以通過線程池來達到這樣的效果。
Exectors
Exectors工廠類提供了線程池的初始化接口捶索,主要有如下幾種(這幾種線程池的使用實例在之前文章都有案例)
newFixedThreadPool
public static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>(),
threadFactory);
}
newFixedThreadPool初始化一個指定線程數(shù)的線程池插掂,線程池里一直存在固定個數(shù)的線程以供調用,其中使用
LinkedBlockingQuene
作為裝載任務線程的阻塞隊列腥例,不過當線程池沒有可執(zhí)行任務時辅甥,也不會釋放線程。
newCachedThreadPool
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
newCachedThreadPool初始化一個可以緩存線程的線程池燎竖,默認緩存60s璃弄,線程池的線程數(shù)可達到Integer.MAX_VALUE,即2147483647构回,內部使用SynchronousQueue作為阻塞隊列,
newCachedThreadPool
在沒有任務執(zhí)行時夏块,當線程的空閑時間超過keepAliveTime
,會自動釋放線程纤掸,當提交新任務時脐供,如果沒有空閑線程,則創(chuàng)建新線程執(zhí)行任務茁肠,會導致一定的系統(tǒng)開銷患民;所以,使用該線程池時垦梆,一定要注意控制并發(fā)的任務數(shù)匹颤,否則創(chuàng)建大量的線程可能導致該線程池線程數(shù)無限增長。
newSingleThreadExecutor
public static ExecutorService newSingleThreadExecutor(ThreadFactory threadFactory) {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>(),
threadFactory));
}
newSingleThreadExecutor初始化的線程池中只有一個線程托猩,如果該線程異常結束印蓖,會重新創(chuàng)建一個新的線程繼續(xù)執(zhí)行任務,唯一的線程可以保證所提交任務的順序執(zhí)行京腥,內部使用LinkedBlockingQueue作為阻塞隊列赦肃。
newScheduledThreadPool
public ScheduledThreadPoolExecutor(int corePoolSize) {
super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
new DelayedWorkQueue());
}
初始化的線程池可以在指定的時間內周期性的執(zhí)行所提交的任務,在實際的業(yè)務場景中可以使用該線程池定期的同步數(shù)據(jù)公浪,內部使用
DelayedWorkQueue
作為阻塞隊列他宛。
上述不同類型的線程池我們通過各自的源碼可以看出,除了newScheduledThreadPool的內部實現(xiàn)特殊一點之外欠气,其它幾個線程池都是基于ThreadPoolExecutor
類實現(xiàn)的厅各。
java.uitl.concurrent.ThreadPoolExecutor類是線程池中最核心的一個類
因此如果要透徹地了解Java中的線程池,必須先了解這個類预柒。下面我們來看一下ThreadPoolExecutor類的具體的實現(xiàn)队塘。
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;
}
下面解釋下一下構造器中各個參數(shù)的含義:
corePoolSize:核心池的大小袁梗,這個參數(shù)跟后面講述的線程池的實現(xiàn)原理有非常大的關系。在創(chuàng)建了線程池后憔古,默認情況下遮怜,線程池中并沒有任何線程,而是等待有任務到來才創(chuàng)建線程去執(zhí)行任務鸿市,除非調用了prestartAllCoreThreads()或者prestartCoreThread()方法锯梁,從這2個方法的名字就可以看出,是預創(chuàng)建線程的意思焰情,即在沒有任務到來之前就創(chuàng)建corePoolSize個線程或者一個線程涝桅。默認情況下,在創(chuàng)建了線程池后烙样,線程池中的線程數(shù)為0,當有任務來之后蕊肥,就會創(chuàng)建一個線程去執(zhí)行任務谒获,當線程池中的線程數(shù)目達到corePoolSize后,就會把到達的任務放到緩存隊列當中壁却;
maximumPoolSize:線程池最大線程數(shù)批狱,這個參數(shù)也是一個非常重要的參數(shù),它表示在線程池中最多能創(chuàng)建多少個線程展东;
keepAliveTime:表示線程沒有任務執(zhí)行時最多保持多久時間會終止赔硫。默認情況下,只有當線程池中的線程數(shù)大于corePoolSize時盐肃,keepAliveTime才會起作用爪膊,直到線程池中的線程數(shù)不大于corePoolSize,即當線程池中的線程數(shù)大于corePoolSize時砸王,如果一個線程空閑的時間達到keepAliveTime推盛,則會終止,直到線程池中的線程數(shù)不超過corePoolSize谦铃。但是如果調用了allowCoreThreadTimeOut(boolean)方法耘成,在線程池中的線程數(shù)不大于corePoolSize時,keepAliveTime參數(shù)也會起作用驹闰,直到線程池中的線程數(shù)為0瘪菌;
unit:參數(shù)keepAliveTime的時間單位,有7種取值嘹朗,在TimeUnit類中有7種靜態(tài)屬性:TimeUnit.DAYS; //天 TimeUnit.HOURS; //小時 TimeUnit.MINUTES; //分鐘 TimeUnit.SECONDS; //秒 TimeUnit.MILLISECONDS; //毫秒 TimeUnit.MICROSECONDS; //微妙 TimeUnit.NANOSECONDS; //納秒
workQueue:一個阻塞隊列师妙,用來存儲等待執(zhí)行的任務,這個參數(shù)的選擇也很重要骡显,會對線程池的運行過程產(chǎn)生重大影響.
threadFactory:創(chuàng)建線程的工廠疆栏,通過自定義的線程工廠可以給每個新建的線程設置一個具有識別度的線程名.
handler:表示當拒絕處理任務時的策略(我們在下一篇詳細分析相關的策略)
ThreadPoolExecutor主要方法
execute()方法:實際上是Executor中聲明的方法曾掂,在ThreadPoolExecutor進行了具體的實現(xiàn),這個方法是ThreadPoolExecutor的核心方法壁顶,通過這個方法可以向線程池提交一個任務珠洗,交由線程池去執(zhí)行。
submit()方法:是在ExecutorService中聲明的方法若专,在AbstractExecutorService就已經(jīng)有了具體的實現(xiàn)许蓖,在ThreadPoolExecutor中并沒有對其進行重寫,這個方法也是用來向線程池提交任務的调衰,但是它和execute()方法不同膊爪,它能夠返回任務執(zhí)行的結果,去看submit()方法的實現(xiàn)嚎莉,會發(fā)現(xiàn)它實際上還是調用的execute()方法米酬,只不過它利用了Future來獲取任務執(zhí)行結果。
shutdown():當線程池調用該方法時,線程池的狀態(tài)則立刻變成SHUTDOWN狀態(tài),以后不能再往線程池中添加任何任務趋箩,否則將會拋出RejectedExecutionException異常赃额。但是,此時線程池不會立刻退出叫确,直到添加到線程池中的任務都已經(jīng)處理完成跳芳,才會退出。
shutdownNow():它通過調用Thread.interrupt來實現(xiàn)線程的立即退出竹勉,不會等待添加到線程池中的任務都已經(jīng)處理完成飞盆。