本文為 Crocutax 原創(chuàng) , 轉(zhuǎn)載請(qǐng)注明出處 http://www.crocutax.com
1.為什么需要線程池
在面向?qū)ο缶幊讨?,創(chuàng)建和銷(xiāo)毀對(duì)象是很耗時(shí)的,因?yàn)閯?chuàng)建一個(gè)對(duì)象要獲取內(nèi)存資源或者其他更多資源.所以在日常編程中才會(huì)有意的避免過(guò)多的創(chuàng)建并不必要的對(duì)象.
線程的創(chuàng)建和銷(xiāo)毀也是同樣,而且相比于普通的對(duì)象更為消耗資源.線程池技術(shù)的引入,就是為了解決這一問(wèn)題.
1.1 線程池簡(jiǎn)介
線程池是指在初始化一個(gè)多線程應(yīng)用程序過(guò)程中創(chuàng)建的一個(gè)線程集合,線程池在任務(wù)未到來(lái)之前,會(huì)創(chuàng)建一定數(shù)量的線程放入空閑隊(duì)列中.這些線程都是處于睡眠狀態(tài),即均未啟動(dòng),因此不消耗CPU,只是占用很小的內(nèi)存空間.當(dāng)請(qǐng)求到來(lái)之后,線程池給這次請(qǐng)求分配一個(gè)空閑線程,把請(qǐng)求傳入此線程中運(yùn)行,進(jìn)行處理.
當(dāng)預(yù)先創(chuàng)建的線程都處于運(yùn)行狀態(tài)時(shí),線程池可以再創(chuàng)建一定數(shù)量的新線程,用于處理更多的任務(wù)請(qǐng)求.
如果線程池中的最大線程數(shù)使用滿(mǎn)了,則會(huì)拋出異常,拒絕請(qǐng)求.當(dāng)系統(tǒng)比較清閑時(shí),也可以通過(guò)移除一部分一直處于停用狀態(tài)的線程,線程池中的每個(gè)線程都有可能被分配多個(gè)任務(wù),一旦任務(wù)完成,線程回到線程池中并等待下一次分配任務(wù).
使用線程池可以提升性能,減少CPU資源的消耗,同時(shí)還可以控制活動(dòng)線程,防止并發(fā)線程過(guò)多,避免內(nèi)存消耗過(guò)度.
1.2 線程池優(yōu)點(diǎn)總結(jié)
- 復(fù)用線程池中的線程,避免因?yàn)榫€程的創(chuàng)建和銷(xiāo)毀所帶來(lái)的性能開(kāi)銷(xiāo)
- 能有效控制線程池的最大并發(fā)數(shù)量,避免大量線程之間因互相搶占系統(tǒng)資源而導(dǎo)致的阻塞現(xiàn)象.
- 能夠?qū)€程進(jìn)行簡(jiǎn)單的管理,并提供定時(shí)執(zhí)行,指定間隔,循環(huán)執(zhí)行等功能.
2.Executor的繼承關(guān)系圖
黃色為接口 , 藍(lán)色為類(lèi)

體系成員簡(jiǎn)介:
-
Executor
線程池體系的頂層接口,只有一個(gè)execute()方法用于執(zhí)行Runnable任務(wù).該體系內(nèi)的所有類(lèi),接口都默認(rèn)實(shí)現(xiàn)/繼承 此接口,并在此基礎(chǔ)上進(jìn)行分類(lèi)擴(kuò)展.
//源碼
public interface Executor {
/**
* @param command Runnable任務(wù)
* @throws RejectedExecutionException 如果任務(wù)無(wú)法繼續(xù)執(zhí)行,則拋出此異常
* @throws NullPointerException 如果傳入的Runnable為null,則拋出此異常
*/
void execute(Runnable command);
}
-
ExecutorService
Executor的擴(kuò)展接口,用于定義一些Runnable管理相關(guān)的方法,比如-
void shutdown();有序關(guān)閉已經(jīng)提交的任務(wù),但是不再接受新的任務(wù),重復(fù)shotdown無(wú)效.而且此方法不會(huì)等待已提交任務(wù)的執(zhí)行完畢. -
List<Runnable> shutdownNow();嘗試停止所有正在執(zhí)行的任務(wù),終止所有處于等待隊(duì)列中的任務(wù),并將這些等待被執(zhí)行的任務(wù)返回給調(diào)用者 -
boolean isShutdown();判斷線程池是否已關(guān)閉 -
boolean isTerminated();當(dāng)調(diào)用了showdown()方法后,所有任務(wù)是否已執(zhí)行結(jié)束.注意:如果不事先調(diào)用showdown()方法,則此方法永遠(yuǎn)返回false. -
boolean awaitTermination(long timeout, TimeUnit unit);當(dāng)調(diào)用shotdown()方法后,調(diào)用此方法可以設(shè)置等待時(shí)間,等待執(zhí)行中的任務(wù)全部結(jié)束,全部結(jié)束返回true.如果超時(shí),或線程中斷導(dǎo)致未全部結(jié)束則返回false. -
<T> Future<T> submit(Callable<T> task);提交有返回值的Runnable任務(wù). -
<T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks,long timeout, TimeUnit unit)執(zhí)行傳入的任務(wù),當(dāng)所有任務(wù)執(zhí)行結(jié)束或超時(shí)后,返回持有任務(wù)狀態(tài)和結(jié)果的Future集合.注意:一個(gè)任務(wù)結(jié)束有兩種情況:1.正常執(zhí)行完成;2.拋出異常. -
<T> T invokeAny(Collection<? extends Callable<T>> tasks);執(zhí)行傳入的任務(wù),只要有任何一個(gè)任務(wù)成功執(zhí)行完成(不拋出異常),一旦有結(jié)果返回,或拋出異常,則其他任務(wù)取消.
-
ScheduledExecutorService
ExecutorService的子接口,定義了延遲或周期性執(zhí)行Runnable任務(wù)的方法.AbstractExecutorService
ExecutorService的默認(rèn)抽象實(shí)現(xiàn)類(lèi),對(duì)ExecutorService進(jìn)行了簡(jiǎn)單實(shí)現(xiàn),開(kāi)發(fā)者可以參考并重寫(xiě)這些方法.SerialExecutorService
ExecutorService的子接口,標(biāo)記型接口,該類(lèi)型的線程池會(huì)以隊(duì)列(先進(jìn)先出)的順序執(zhí)行提交的任務(wù).ThreadPoolExecutor
AbstractExecutorService子接口的默認(rèn)實(shí)現(xiàn)類(lèi),可以使用這個(gè)類(lèi)自定義線程池使用.系統(tǒng)提供的幾種常用線程池,最終都是通過(guò)此類(lèi)來(lái)創(chuàng)建.ScheduledThreadPoolExecutor
ScheduledExecutorService子接口的實(shí)現(xiàn)類(lèi),繼承ThreadPoolExecutor,用于延遲或周期性執(zhí)行Runnable任務(wù).
3.如何創(chuàng)建線程池
Executor是Java中的一個(gè)接口,其默認(rèn)實(shí)現(xiàn)類(lèi)是ThreadPoolExecutor,構(gòu)造方法如下
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)
ThreadPoolExecutor一共有4個(gè)重載的構(gòu)造方法,上述代碼中的后三位參數(shù)都是可選參數(shù).
通過(guò)構(gòu)造方法即可自定義線程池.然后通過(guò)execute()來(lái)執(zhí)行Runnable任務(wù).
涉及到的幾個(gè)參數(shù)解釋如下:
- corePoolSize
線程池的核心線程數(shù),默認(rèn)情況下,核心線程會(huì)在線程池中一直存活,即使它們處于閑置狀態(tài).如果將ThreadPoolExecutor的allowCoreThreadTimeOut屬性設(shè)置為true,那么閑置的[核心線程]在等待新任務(wù)到來(lái)時(shí)會(huì)有超時(shí)策略,這個(gè)時(shí)間間隔由keepAliveTime所指定,當(dāng)?shù)却龝r(shí)間超過(guò)keepAliveTime所指定的時(shí)長(zhǎng)后,核心線程就會(huì)被終止.
- maximumPoolSize
線程池所能容納的最大線程數(shù),當(dāng)活動(dòng)線程數(shù)達(dá)到這個(gè)數(shù)值后,后續(xù)的新任務(wù)將會(huì)被阻塞.
- keepAliveTime
非核心線程閑置時(shí)的超時(shí)時(shí)長(zhǎng),超過(guò)這個(gè)時(shí)長(zhǎng),非核心線程就會(huì)被回收.當(dāng)ThreadPoolExecutor的allowCoreThreadTimeOut屬性設(shè)置為true時(shí), keepAliveTime同樣會(huì)作用于核心線程.
這個(gè)[?;顣r(shí)間]設(shè)計(jì)的巧妙之處在于:當(dāng)線程處于閑置狀態(tài)時(shí),并不馬上銷(xiāo)毀,而是在指定時(shí)間段內(nèi)將其緩存在線程池中,以方便在限定的時(shí)間段內(nèi)如果再有任務(wù)來(lái)臨,能夠快速的重新啟用等待中的線程.處于閑置狀態(tài)的空閑線程并不會(huì)占用多少內(nèi)存,而且這樣就能顯著減少頻繁的創(chuàng)建,銷(xiāo)毀線程造成的內(nèi)存消耗及性能下降.
- unit
用于指定keepAliveTime參數(shù)的時(shí)間單位,這是一個(gè)枚舉,常用的有TimeUnit.NANOSECONDS(毫秒),TimeUnit.SECONDS(秒),TimeUnit.MINUTES(分鐘)等
- workQueue
線程池中等待被執(zhí)行的任務(wù)隊(duì)列,這個(gè)隊(duì)列僅持有通過(guò)execute方法提交的Runnable任務(wù).
- threadFactory
線程工廠,ThreadFactory是個(gè)接口,它只有一個(gè)方法,·Thread newThread(Runnable r),executor創(chuàng)建新線程時(shí)調(diào)用
- RejectedExecutionHandler 當(dāng)由于線程阻塞,任務(wù)隊(duì)列容量已滿(mǎn)等因素導(dǎo)致無(wú)法成功執(zhí)行任務(wù)時(shí),這個(gè)handler會(huì)調(diào)用rejectedExecution方法來(lái)通知調(diào)用者.
4.系統(tǒng)封裝的4種線程池
這里要使用到Executors類(lèi),它是Executor體系的靜態(tài)工廠類(lèi),類(lèi)中封裝了一些創(chuàng)建線程池的方法.
Executor與其子接口及其實(shí)現(xiàn)類(lèi)負(fù)責(zé)定義和規(guī)范線程池,而Executors負(fù)責(zé)創(chuàng)建線程池.
4.1 SingleThreadExecutor
SingleThreadExecutor內(nèi)部只有一個(gè)核心線程,它確保所有的任務(wù)都在同一個(gè)線程中按順序執(zhí)行.
它的意義在于統(tǒng)一所有的外界任務(wù)到一個(gè)線程中,使得在這些任務(wù)之間不需要處理線程同步的問(wèn)題.
內(nèi)部實(shí)現(xiàn)如下:
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
代碼測(cè)試:
private void singleThreadExecutorTest() {
//單一線程池
ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();
//開(kāi)啟5次線程
for (int i = 0; i < 5; i++) {
singleThreadExecutor.execute(new MyThread());
}
//關(guān)閉線程池
singleThreadExecutor.shutdown();
}
Log輸出:
System.out: pool-1-thread-1 執(zhí)行
System.out: pool-1-thread-1 執(zhí)行
System.out: pool-1-thread-1 執(zhí)行
System.out: pool-1-thread-1 執(zhí)行
System.out: pool-1-thread-1 執(zhí)行
4.2 FixedThreadPoolExecutor
FixedThreadPoolExecutor是一種線程數(shù)量固定的線程池,只有核心線程,沒(méi)有超時(shí)機(jī)制,任務(wù)隊(duì)列沒(méi)有大小限制.
當(dāng)所有的線程都處于活動(dòng)狀態(tài)時(shí),新任務(wù)都會(huì)處于等待狀態(tài),直到有線程空閑出來(lái).
當(dāng)線程處于空閑狀態(tài)時(shí),它們并不會(huì)被回收,除非線程池被關(guān)閉了.這意味著它能夠更加快速的響應(yīng)外界的請(qǐng)求.
//構(gòu)造方法
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
代碼測(cè)試:
/**自定義一個(gè)線程類(lèi),繼承Thread,打印Log*/
class MyThread extends Thread{
public void run() {
System.out.println(Thread.currentThread().getName()+" 執(zhí)行");
}
}
private void fixedThreadPoolTest() {
//容量為2的固定線程池
ExecutorService fixedThreadPool = Executors.newFixedThreadPool(2);
//創(chuàng)建5條線程
for (int i = 0; i < 5; i++) {
fixedThreadPool.execute(new MyThread());
}
//關(guān)閉線程池
fixedThreadPool.shutdown();
}
Log輸出:
System.out: pool-1-thread-1 執(zhí)行
System.out: pool-1-thread-2 執(zhí)行
System.out: pool-1-thread-2 執(zhí)行
System.out: pool-1-thread-2 執(zhí)行
System.out: pool-1-thread-1 執(zhí)行
可以發(fā)現(xiàn),雖然new了5個(gè)Thread,但是系統(tǒng)只用兩條線程來(lái)執(zhí)行5個(gè)任務(wù).
4.3 CachedThreadPool
CachedThreadPool線程數(shù)量不定,只有非核心線程,并且其最大線程數(shù)為Integer.MAX_VALUE(相當(dāng)于無(wú)限大)
當(dāng)線程池中的線程都處于活動(dòng)狀態(tài)時(shí),線程池會(huì)創(chuàng)建新的線程來(lái)處理新任務(wù),否則就會(huì)利用空閑的線程來(lái)處理新任務(wù).
空閑線程都有超時(shí)時(shí)機(jī),這個(gè)超時(shí)時(shí)長(zhǎng)為60秒,限制超60秒就會(huì)被回收.
和FixedThreadPoolExecutor不同的是,CachedThreadPool的任務(wù)隊(duì)列其實(shí)相當(dāng)一個(gè)空集合,這將導(dǎo)致任何任務(wù)都會(huì)被立即執(zhí)行,因?yàn)榇藭r(shí)SynchronousQueue是無(wú)法插入任務(wù)的.
CachedThreadPool線程池比較適合執(zhí)行大量+耗時(shí)較少的任務(wù).當(dāng)整個(gè)線程池都處于閑置狀態(tài)時(shí),會(huì)因超時(shí)而被停止,此時(shí)沒(méi)有線程的線程池幾乎不占用任何系統(tǒng)資源.
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
代碼測(cè)試:
private void cachedThreadPoolTest() {
//緩存線程池
ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
//創(chuàng)建5條線程
for (int i = 0; i < 5; i++) {
cachedThreadPool.execute(new MyThread());
}
//關(guān)閉線程池
cachedThreadPool.shutdown();
}
Log輸出:
System.out: pool-1-thread-1執(zhí)行
System.out: pool-1-thread-1執(zhí)行
System.out: pool-1-thread-2執(zhí)行
System.out: pool-1-thread-3執(zhí)行
System.out: pool-1-thread-4執(zhí)行
4.4 ScheduledThreadPoolExecutor
ScheduledThreadPoolExecutor核心線程數(shù)固定,非核心線程數(shù)沒(méi)有限制,并且非核心線程閑置時(shí)會(huì)被回收
主要用于執(zhí)行定時(shí)任務(wù) 和 具有固定周期的重復(fù)任務(wù).
//Executors靜態(tài)方法
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
return new ScheduledThreadPoolExecutor(corePoolSize);
}
//繼續(xù)追查ScheduledThreadPoolExecutor源碼,會(huì)發(fā)現(xiàn)最終還是通過(guò)ThreadPoolExecutor的構(gòu)造來(lái)創(chuàng)建
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), defaultHandler);
}
常用方法:
-
schedule(Runnable command,long delay, TimeUnit unit)XXX時(shí)間之后,執(zhí)行指定的任務(wù) -
scheduleAtFixedRate(Runnable command,long initialDelay,long period,TimeUnit unit)
XXX時(shí)間后,執(zhí)行指定任務(wù),每隔XXX時(shí)間執(zhí)行一次
代碼測(cè)試:
private void scheduledThreadPoolTest() {
//定時(shí)任務(wù)線程池
ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(1);
//3秒后打印一次Log
scheduledThreadPool.schedule(new Runnable() {
@Override
public void run() {
System.out.println("~~~~~~~~~3秒之后露個(gè)臉~~~~~~~~~");
}
},3000,TimeUnit.MILLISECONDS);
//0秒初始化延遲,每秒打印Log
scheduledThreadPool.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
System.out.println("每隔一秒打印一次");
}
},0,1000, TimeUnit.MILLISECONDS);
}
Log輸出:
System.out: 每隔一秒打印一次
System.out: 每隔一秒打印一次
System.out: 每隔一秒打印一次
System.out: ~~~~~~~~~3秒之后露個(gè)臉~~~~~~~~~
System.out: 每隔一秒打印一次
System.out: 每隔一秒打印一次
System.out: 每隔一秒打印一次
5.總結(jié)
可以發(fā)現(xiàn),系統(tǒng)提供的4種線程池,最終都是通過(guò)配置ThreadPoolExecutor的不同參數(shù),來(lái)巧妙的達(dá)到不同的線程管理效果.
以上只是關(guān)于線程池的一些基礎(chǔ)認(rèn)知,下一篇進(jìn)行 線程池運(yùn)行原理分析.