為什么用線程池?
- 1.創(chuàng)建/銷毀線程伴隨著系統(tǒng)開(kāi)銷搬男,過(guò)于頻繁的創(chuàng)建/銷毀線程,會(huì)很大程度上影響處-理效率
- 2.線程并發(fā)數(shù)量過(guò)多举塔,搶占系統(tǒng)資源從而導(dǎo)致阻塞
- 3.對(duì)線程進(jìn)行一些簡(jiǎn)單的管理
在Java中输玷,線程池的概念是Executor這個(gè)接口,具體實(shí)現(xiàn)為ThreadPoolExecutor類立砸,學(xué)習(xí)Java中的線程池,就可以直接學(xué)習(xí)他了對(duì)線程池的配置,就是對(duì)ThreadPoolExecutor構(gòu)造函數(shù)的參數(shù)的配置
一呀页、ThreadPoolExecutor提供了四個(gè)構(gòu)造函數(shù)
//五個(gè)參數(shù)的構(gòu)造函數(shù)
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue)
//六個(gè)參數(shù)的構(gòu)造函數(shù)-1
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory)
//六個(gè)參數(shù)的構(gòu)造函數(shù)-2
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
RejectedExecutionHandler handler)
//七個(gè)參數(shù)的構(gòu)造函數(shù)
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)
下面來(lái)解釋下各個(gè)參數(shù):
- int corePoolSize:該線程池中核心線程數(shù)最大值
核心線程:線程池新建線程的時(shí)候,如果當(dāng)前線程總數(shù)小于corePoolSize拥坛,則新建的是核心線程蓬蝶,如果超過(guò)corePoolSize尘分,則新建的是非核心線程核心線程默認(rèn)情況下會(huì)一直存活在線程池中,即使這個(gè)核心線程啥也不干(閑置狀態(tài))丸氛。
如果指定ThreadPoolExecutor的allowCoreThreadTimeOut這個(gè)屬性為true培愁,那么核心線程如果不干活(閑置狀態(tài))的話,超過(guò)一定時(shí)間(時(shí)長(zhǎng)下面參數(shù)決定)缓窜,就會(huì)被銷毀掉定续。
- int maximumPoolSize: 該線程池中線程總數(shù)最大值
線程總數(shù) = 核心線程數(shù) + 非核心線程數(shù)。
- long keepAliveTime:該線程池中非核心線程閑置超時(shí)時(shí)長(zhǎng)
一個(gè)非核心線程禾锤,如果不干活(閑置狀態(tài))的時(shí)長(zhǎng)超過(guò)這個(gè)參數(shù)所設(shè)定的時(shí)長(zhǎng)私股,就會(huì)被銷毀掉,如果設(shè)置allowCoreThreadTimeOut = true恩掷,則會(huì)作用于核心線程倡鲸。
- TimeUnit unit:keepAliveTime的單位
TimeUnit是一個(gè)枚舉類型,其包括:
NANOSECONDS : 1微毫秒 = 1微秒 / 1000
MICROSECONDS : 1微秒 = 1毫秒 / 1000
MILLISECONDS : 1毫秒 = 1秒 /1000
SECONDS : 秒
MINUTES : 分
HOURS : 小時(shí)
DAYS : 天
- BlockingQueue workQueue:該線程池中的任務(wù)隊(duì)列:維護(hù)著等待執(zhí)行的Runnable對(duì)象
當(dāng)所有的核心線程都在干活時(shí)黄娘,新添加的任務(wù)會(huì)被添加到這個(gè)隊(duì)列中等待處理峭状,如果隊(duì)列滿了,則新建非核心線程執(zhí)行任務(wù)逼争。
常用的workQueue類型:
SynchronousQueue:這個(gè)隊(duì)列接收到任務(wù)的時(shí)候优床,會(huì)直接提交給線程處理,而不保留它氮凝,如果所有線程都在工作怎么辦羔巢?那就新建一個(gè)線程來(lái)處理這個(gè)任務(wù)!所以為了保證不出現(xiàn)<線程數(shù)達(dá)到了maximumPoolSize而不能新建線程>的錯(cuò)誤罩阵,使用這個(gè)類型隊(duì)列的時(shí)候竿秆,maximumPoolSize一般指定成Integer.MAX_VALUE,即無(wú)限大
LinkedBlockingQueue:這個(gè)隊(duì)列接收到任務(wù)的時(shí)候稿壁,如果當(dāng)前線程數(shù)小于核心線程數(shù)幽钢,則新建線程(核心線程)處理任務(wù);如果當(dāng)前線程數(shù)等于核心線程數(shù)傅是,則進(jìn)入隊(duì)列等待匪燕。由于這個(gè)隊(duì)列沒(méi)有最大值限制,即所有超過(guò)核心線程數(shù)的任務(wù)都將被添加到隊(duì)列中喧笔,這也就導(dǎo)致了maximumPoolSize的設(shè)定失效帽驯,因?yàn)榭偩€程數(shù)永遠(yuǎn)不會(huì)超過(guò)corePoolSize
ArrayBlockingQueue:可以限定隊(duì)列的長(zhǎng)度,接收到任務(wù)的時(shí)候书闸,如果沒(méi)有達(dá)到corePoolSize的值尼变,則新建線程(核心線程)執(zhí)行任務(wù),如果達(dá)到了浆劲,則入隊(duì)等候嫌术,如果隊(duì)列已滿哀澈,則新建線程(非核心線程)執(zhí)行任務(wù),又如果總線程數(shù)到了maximumPoolSize度气,并且隊(duì)列也滿了割按,則發(fā)生錯(cuò)誤
DelayQueue:隊(duì)列內(nèi)元素必須實(shí)現(xiàn)Delayed接口,這就意味著你傳進(jìn)去的任務(wù)必須先實(shí)現(xiàn)Delayed接口磷籍。這個(gè)隊(duì)列接收到任務(wù)時(shí)适荣,首先先入隊(duì),只有達(dá)到了指定的延時(shí)時(shí)間择示,才會(huì)執(zhí)行任務(wù)
ThreadFactory threadFactory:創(chuàng)建線程的方式束凑,這是一個(gè)接口,你new他的時(shí)候需要實(shí)現(xiàn)他的Thread newThread(Runnable r)方法栅盲,一般用不上汪诉。
RejectedExecutionHandler handler:這玩意兒就是拋出異常專用的,比如上面提到的兩個(gè)錯(cuò)誤發(fā)生了谈秫,就會(huì)由這個(gè)handler拋出異常扒寄,根本用不上。
二拟烫、向ThreadPoolExecutor添加任務(wù)
我們?cè)趺粗纍ew一個(gè)ThreadPoolExecutor该编,大概知道各個(gè)參數(shù)是干嘛的,可是我new完了硕淑,怎么向線程池提交一個(gè)要執(zhí)行的任務(wù)翱慰ⅰ?
ThreadPoolExecutor.execute(Runnable command)
通過(guò)ThreadPoolExecutor.execute(Runnable command)方法即可向線程池內(nèi)添加一個(gè)任務(wù)置媳。
三于樟、ThreadPoolExecutor的策略
這里給總結(jié)一下,當(dāng)一個(gè)任務(wù)被添加進(jìn)線程池時(shí)拇囊,執(zhí)行策略:
- 1.線程數(shù)量未達(dá)到corePoolSize迂曲,則新建一個(gè)線程(核心線程)執(zhí)行任務(wù)
- 2.線程數(shù)量達(dá)到了corePools,則將任務(wù)移入隊(duì)列等待
- 3.隊(duì)列已滿寥袭,新建線程(非核心線程)執(zhí)行任務(wù)
- 4.隊(duì)列已滿路捧,總線程數(shù)又達(dá)到了maximumPoolSize,就會(huì)由(RejectedExecutionHandler)拋出異常
+++++++++++++++++++++++++++我是分割線++++++++++++++++++++++++++++
常見(jiàn)四種線程池:
如果你不想自己寫一個(gè)線程池传黄,Java通過(guò)Executors提供了四種線程池杰扫,這四種線程池都是直接或間接配置ThreadPoolExecutor的參數(shù)實(shí)現(xiàn)的。
1.可緩存線程池CachedThreadPool()
源碼:
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
根據(jù)源碼可以看出:
- 這種線程池內(nèi)部沒(méi)有核心線程膘掰,線程的數(shù)量是有沒(méi)限制的涉波。
- 在創(chuàng)建任務(wù)時(shí),若有空閑的線程時(shí)則復(fù)用空閑的線程炭序,若沒(méi)有則新建線程啤覆。
- 沒(méi)有工作的線程(閑置狀態(tài))在超過(guò)了60S還不做事,就會(huì)銷毀惭聂。
創(chuàng)建方法:
ExecutorService mCachedThreadPool = Executors.newCachedThreadPool();
用法:
//開(kāi)始下載
private void startDownload(final ProgressBar progressBar, final int i) {
mCachedThreadPool.execute(new Runnable() {
@Override
public void run() {
int p = 0;
progressBar.setMax(10);//每個(gè)下載任務(wù)10秒
while (p < 10) {
p++;
progressBar.setProgress(p);
Bundle bundle = new Bundle();
Message message = new Message();
bundle.putInt("p", p);
//把當(dāng)前線程的名字用handler讓textview顯示出來(lái)
bundle.putString("ThreadName", Thread.currentThread().getName());
message.what = i;
message.setData(bundle);
mHandler.sendMessage(message);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
});
}
2.FixedThreadPool 定長(zhǎng)線程池
源碼:
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
根據(jù)源碼可以看出:
- 該線程池的最大線程數(shù)等于核心線程數(shù)窗声,所以在默認(rèn)情況下,該線程池的線程不會(huì)因?yàn)殚e置狀態(tài)超時(shí)而被銷毀辜纲。
- 如果當(dāng)前線程數(shù)小于核心線程數(shù)笨觅,并且也有閑置線程的時(shí)候提交了任務(wù),這時(shí)也不會(huì)去復(fù)用之前的閑置線程耕腾,會(huì)創(chuàng)建新的線程去執(zhí)行任務(wù)见剩。如果當(dāng)前執(zhí)行任務(wù)數(shù)大于了核心線程數(shù),大于的部分就會(huì)進(jìn)入隊(duì)列等待扫俺。等著有閑置的線程來(lái)執(zhí)行這個(gè)任務(wù)苍苞。
創(chuàng)建方法:
//nThreads => 最大線程數(shù)即maximumPoolSize
ExecutorService mFixedThreadPool= Executors.newFixedThreadPool(int nThreads);
//threadFactory => 創(chuàng)建線程的方法,用得少
ExecutorService mFixedThreadPool= Executors.newFixedThreadPool(int nThreads, ThreadFactory threadFactory);
用法:
private void startDownload(final ProgressBar progressBar, final int i) {
mFixedThreadPool.execute(new Runnable() {
@Override
public void run() {
//....邏輯代碼自己控制
}
});
}
3.SingleThreadPool
源碼:
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
根據(jù)源碼可以看出:
- 有且僅有一個(gè)工作線程執(zhí)行任務(wù)
- 所有任務(wù)按照指定順序執(zhí)行狼纬,即遵循隊(duì)列的入隊(duì)出隊(duì)規(guī)則
創(chuàng)建方法:
ExecutorService mSingleThreadPool = Executors.newSingleThreadPool();
用法同上羹呵。
4.ScheduledThreadPool
源碼:
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
return new ScheduledThreadPoolExecutor(corePoolSize);
}
//ScheduledThreadPoolExecutor():
public ScheduledThreadPoolExecutor(int corePoolSize) {
super(corePoolSize, Integer.MAX_VALUE,
DEFAULT_KEEPALIVE_MILLIS, MILLISECONDS,
new DelayedWorkQueue());
}
根據(jù)源碼可以看出:
DEFAULT_KEEPALIVE_MILLIS就是默認(rèn)10L,這里就是10秒疗琉。這個(gè)線程池有點(diǎn)像是吧CachedThreadPool和FixedThreadPool 結(jié)合了一下冈欢。
- 不僅設(shè)置了核心線程數(shù),最大線程數(shù)也是Integer.MAX_VALUE盈简。
- 這個(gè)線程池是上述4個(gè)中為唯一個(gè)有延遲執(zhí)行和周期執(zhí)行任務(wù)的線程池凑耻。
創(chuàng)建:
//nThreads => 最大線程數(shù)即maximumPoolSize
ExecutorService mScheduledThreadPool = Executors.newScheduledThreadPool(int corePoolSize);
一般的執(zhí)行任務(wù)方法和上面的都大同小異,我們主要看看延時(shí)執(zhí)行任務(wù)和周期執(zhí)行任務(wù)的方法柠贤。
//表示在3秒之后開(kāi)始執(zhí)行我們的任務(wù)香浩。
mScheduledThreadPool.schedule(new Runnable() {
@Override
public void run() {
//....
}
}, 3, TimeUnit.SECONDS);
//延遲3秒后執(zhí)行任務(wù),從開(kāi)始執(zhí)行任務(wù)這個(gè)時(shí)候開(kāi)始計(jì)時(shí)种吸,每7秒執(zhí)行一次不管執(zhí)行任務(wù)需要多長(zhǎng)的時(shí)間弃衍。
mScheduledThreadPool.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
//....
}
},3, 7, TimeUnit.SECONDS);
/**延遲3秒后執(zhí)行任務(wù),從任務(wù)完成時(shí)這個(gè)時(shí)候開(kāi)始計(jì)時(shí)坚俗,7秒后再執(zhí)行镜盯,
*再等完成后計(jì)時(shí)7秒再執(zhí)行也就是說(shuō)這里的循環(huán)執(zhí)行任務(wù)的時(shí)間點(diǎn)是
*從上一個(gè)任務(wù)完成的時(shí)候。
*/
mScheduledThreadPool.scheduleWithFixedDelay(new Runnable() {
@Override
public void run() {
//....
}
},3, 7, TimeUnit.SECONDS);
以上就是常用的四個(gè)線程池以及他們的實(shí)現(xiàn)原理猖败。