新聞中心
今天我們來(lái)聊一下這個(gè) Java 中的線程池,線程池,這塊的內(nèi)容,已經(jīng)是非常的容易被面試官問(wèn)到的內(nèi)容,為什么呢?這是因?yàn)榫€程池,是一種多線程的處理方式,如果使用方式得當(dāng)?shù)脑?,那么?duì)我們的代碼的質(zhì)量也是非常高的。

專注于為中小企業(yè)提供做網(wǎng)站、成都網(wǎng)站建設(shè)服務(wù),電腦端+手機(jī)端+微信端的三站合一,更高效的管理,為中小企業(yè)洪江管理區(qū)免費(fèi)做網(wǎng)站提供優(yōu)質(zhì)的服務(wù)。我們立足成都,凝聚了一批互聯(lián)網(wǎng)行業(yè)人才,有力地推動(dòng)了上千多家企業(yè)的穩(wěn)健成長(zhǎng),幫助中小企業(yè)通過(guò)網(wǎng)站建設(shè)實(shí)現(xiàn)規(guī)模擴(kuò)充和轉(zhuǎn)變。
我們既然要了解線程池,那么肯定是需要從幾個(gè)角度來(lái)考慮,第一,什么是線程池?第二:為什么需要線程池?第三,線程池的創(chuàng)建方式都有哪些。
什么是線程池
線程池是一種多線程處理形式,處理過(guò)程中將任務(wù)添加到隊(duì)列,然后在創(chuàng)建線程后自動(dòng)啟動(dòng)這些任務(wù)。
線程池線程都是后臺(tái)線程。每個(gè)線程都使用默認(rèn)的堆棧大小,以默認(rèn)的優(yōu)先級(jí)運(yùn)行,并處于多線程單元 中。如果某個(gè)線程在托管代碼中空閑(如正在等待某個(gè)事件),則線程池將插入另一個(gè)輔助線程來(lái)使所 有處理器保持繁忙。
如果所有線程池線程都始終保持繁忙,但隊(duì)列中包含掛起的工作,則線程池將在一段時(shí)間后創(chuàng)建另一個(gè)輔助線程但線程的數(shù)目永遠(yuǎn)不會(huì)超過(guò)最大值。超過(guò)最大值的線程可以排隊(duì),但他們要等到其他線程完成后才啟動(dòng)。
為什么需要線程池
我們有兩種常見(jiàn)的創(chuàng)建線程的方法,
- 一種是繼承Thread類,
- 一種是實(shí)現(xiàn)Runnable的接口,Thread類其實(shí)也是實(shí)現(xiàn)了Runnable接口。
但是我們創(chuàng)建這兩種線程在運(yùn)行結(jié)束后都會(huì)被虛擬機(jī)銷毀,如果線程數(shù)量多的話,頻繁的創(chuàng)建和銷毀線程會(huì)大大浪費(fèi)時(shí)間和效率,更重要的是浪費(fèi)內(nèi)存。那么有沒(méi)有一種方法能讓線程運(yùn)行完后不立即銷毀,而是讓線程重復(fù)使用,繼續(xù)執(zhí)行其他的任務(wù)哪?
這就是線程池的由來(lái),很好的解決線程的重復(fù)利用,避免重復(fù)開(kāi)銷。
線程池的優(yōu)點(diǎn)都有哪些?
(1)重用存在的線程,減少對(duì)象創(chuàng)建銷毀的開(kāi)銷。
(2)可有效的控制最大并發(fā)線程數(shù),提高系統(tǒng)資源的使用率,同時(shí)避免過(guò)多資源競(jìng)爭(zhēng),避免堵塞。
(3)提供定時(shí)執(zhí)行、定期執(zhí)行、單線程、并發(fā)數(shù)控制等功能。
而且優(yōu)點(diǎn)這么多,那么肯定就得知道 Java 提供了哪些線程池的實(shí)現(xiàn)方式了。
Java 提供了哪幾種線程池
種類一
FixedThreadPool
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue());
} newFixedThreadPool 實(shí)際上返回的是 ThreadPoolExecutor,ThreadPoolExecutor 實(shí)際就是線程池實(shí)現(xiàn)類,使用了典型的模板方法設(shè)計(jì)模式,通過(guò)ThreadPoolExecutor構(gòu)造器的說(shuō)明我們稍微解釋一下各參數(shù)的意思:
構(gòu)造器如下:
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue workQueue) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), defaultHandler);
} - corePoolSize 核心線程數(shù),線程池一直存在的線程(即使這些線程是空閑的),除非你設(shè)置allowCoreThreadTimeOut
- maximumPoolSize 線程池中允許存在的最多的線程數(shù),當(dāng)workQueue滿了之后會(huì)創(chuàng)建線程直到數(shù)量達(dá)到maximumPoolSize
- keepAliveTime 當(dāng)線程池中的線程數(shù)超過(guò)核心線程數(shù),超過(guò)部分的線程可以空閑keepAliveTime時(shí)間,如果這段時(shí)間還沒(méi)有任務(wù)過(guò)來(lái)則超過(guò)部分線程就會(huì)銷毀
- unit keepAliveTime的單位
- workQueue 等待隊(duì)列,當(dāng)corePoolSize線程都在處理任務(wù)時(shí),新進(jìn)入線程池的任務(wù)則翻入等待下隊(duì)列
- threadFactory 創(chuàng)建線程池中線程的線程工廠
- handler 拒絕策略 當(dāng)線程池已達(dá)到最大線程數(shù)和等待隊(duì)列也已經(jīng)滿了,則使用拒絕策略
而newFixedThreadPool使用了corePoolSize和maximumPoolSize相等,所以當(dāng)線程池線程數(shù)到達(dá)corePoolSize之后就不會(huì)再創(chuàng)建線程。
并且newFixedThreadPool使用了LinkedBlockingQueue,而且使用的是默認(rèn)構(gòu)造器,容量是Integer.MAX_VALUE,keepAliveTime是0,實(shí)際上設(shè)不設(shè)置都沒(méi)關(guān)系,因?yàn)椴粫?huì)超過(guò)核心線程數(shù)。
實(shí)際上我們也可以理解為FixedThreadPool該線程池中的線程數(shù)量始不變。當(dāng)有一個(gè)新的任務(wù)提交時(shí),線程池中若有空閑線程,則立即執(zhí)行。若沒(méi)有,則新的任務(wù)會(huì)被暫存在一個(gè)任務(wù)隊(duì)列中,待有線程空閑時(shí),便處理在任務(wù)隊(duì)列中的任務(wù)。
種類二
SingleThreadExecutor
實(shí)際上SingleThreadExecutor是使用單個(gè) worker 線程的Executor。
也就是說(shuō)方法返回一個(gè)只有一個(gè)線程的線程池。若多余一個(gè)任務(wù)被提交到該線程 池,任務(wù)會(huì)被保存在一個(gè)任務(wù)隊(duì)列中,待線程空閑,按先入先出的順序執(zhí)行隊(duì)列中的任務(wù)
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue()));
} SingleThreadExecutor 的 corePoolSize 和 maximumPoolSize 被設(shè)置為 1 。其他參數(shù)與 FixedThreadPool 相同。 SingleThreadExecutor 使用無(wú) 隊(duì)列 LinkedBlockingQueue 作 為線程池的工作隊(duì)列( 隊(duì)列的容量為Integer.MAX_VALUE )。
種類三
CachedThreadPool
該方法返回一個(gè)可根據(jù)實(shí)際情況調(diào)整線程數(shù)量的線程池。線程池的線程數(shù) 量不確定,但若有空閑線程可以復(fù)用,則會(huì)優(yōu)先使用可復(fù)用的線程。若所有線程均在工作,又有新 的任務(wù)提交,則會(huì)創(chuàng)建新的線程處理任務(wù)。所有線程在當(dāng)前任務(wù)執(zhí)行完畢后,將返回線程池進(jìn)行復(fù) 用。
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue());
} - 這個(gè)線程池corePoolSize是0,maximumPoolSize是Integer.MAX_VALUE,根據(jù)我們之前對(duì)源碼的分析,CachedThreadPool不會(huì)有核心線程,核心線程數(shù)的addworker方法不會(huì)執(zhí)行,直接嘗試加入queue中。
- keepAliveTime是60s,也就是60s從隊(duì)列中沒(méi)有拿到任務(wù),worker就會(huì)自動(dòng)銷毀。
- queue使用的是SynchronousQueue,這個(gè)就是CachedThreadPool實(shí)現(xiàn)不停創(chuàng)建工作線程的關(guān)鍵,因?yàn)榫€程池處理任務(wù)分為三步:有任務(wù)過(guò)來(lái)先創(chuàng)建核心線程,當(dāng)線程數(shù)達(dá)到核心線程數(shù)時(shí)則會(huì)進(jìn)入等待隊(duì)列,當(dāng)?shù)却?duì)列滿了則再創(chuàng)建非核心線程,直到線程數(shù)達(dá)到最大線程數(shù)后執(zhí)行拒絕策略,所以可以猜測(cè)SynchronousQueue應(yīng)該是沒(méi)有容量的隊(duì)列,放入一個(gè)offer操作必須有一個(gè)poll操作在等待,沒(méi)有的話就執(zhí)行入隊(duì)失敗,然后創(chuàng)建非核心線程進(jìn)行處理
- 線程工廠和拒絕策略都沒(méi)有指定,則使用的就是默認(rèn)的,默認(rèn)的拒絕策略是丟出一個(gè)異常
種類四
ScheduledThreadPoolExecutor
這個(gè)線程池的構(gòu)造器就比較多了
// 第一個(gè)構(gòu)造函數(shù)
public ScheduledThreadPoolExecutor(int corePoolSize) {
super(corePoolSize, Integer.MAX_VALUE,
DEFAULT_KEEPALIVE_MILLIS, MILLISECONDS,
new DelayedWorkQueue());
}
// 第二個(gè)構(gòu)造函數(shù)
public ScheduledThreadPoolExecutor(int corePoolSize,
ThreadFactory threadFactory) {
super(corePoolSize, Integer.MAX_VALUE,
DEFAULT_KEEPALIVE_MILLIS, MILLISECONDS,
new DelayedWorkQueue(), threadFactory);
}
// 第三個(gè)構(gòu)造函數(shù)
public ScheduledThreadPoolExecutor(int corePoolSize,
RejectedExecutionHandler handler) {
super(corePoolSize, Integer.MAX_VALUE,
DEFAULT_KEEPALIVE_MILLIS, MILLISECONDS,
new DelayedWorkQueue(), handler);
}
// 第四個(gè)構(gòu)造函數(shù)
public ScheduledThreadPoolExecutor(int corePoolSize,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) {
super(corePoolSize, Integer.MAX_VALUE,
DEFAULT_KEEPALIVE_MILLIS, MILLISECONDS,
new DelayedWorkQueue(), threadFactory, handler);
}主要用來(lái)在給定的延遲后運(yùn)行任務(wù),或者定期執(zhí)行任務(wù)。ScheduledThreadPoolExecutor又分為:ScheduledThreadPoolExecutor(包含多個(gè)線程)和SingleThreadScheduledExecutor (只包含一個(gè)線程)兩種。
線程池的創(chuàng)建方式
其實(shí)創(chuàng)建方式,就是Executors,使用 Executors 可以非常輕易的創(chuàng)建我們上面所說(shuō)的這幾種線程池。但是呢,有一個(gè)地方不知道大家有沒(méi)有注意到,在 阿里的開(kāi)發(fā)手冊(cè)上面,是不允許使用 Executors 去創(chuàng)建的,更推薦我們使用的是 ThreadPoolExecutor 構(gòu)造去創(chuàng)建,這樣的好處就是為了規(guī)避資源耗盡的風(fēng)險(xiǎn)那時(shí)候拋出的就不是異常,而是錯(cuò)誤了。
使用ThreadPoolExecutor的構(gòu)造函數(shù)創(chuàng)建
private static ExecutorService executor = new ThreadPoolExecutor(13, 13,
60L, TimeUnit.SECONDS,
new ArrayBlockingQueue(13));我們可以自己直接調(diào)用 ThreadPoolExecutor 的構(gòu)造函數(shù)來(lái)自己創(chuàng)建線程池。在創(chuàng)建的同時(shí),給 BlockQueue 指定容量就可以了。
這時(shí)候如果提交的線程數(shù)超過(guò)了可用線程數(shù)的時(shí)候,就會(huì)拋出異常,比如 java.util.concurrent.RejectedExecutionException,這是因?yàn)楫?dāng)前線程池使用的隊(duì)列是有邊界隊(duì)列,隊(duì)列已經(jīng)滿了便無(wú)法繼續(xù)處理新的請(qǐng)求。
如果你覺(jué)得對(duì) Error 和 Exception 沒(méi)有任何感覺(jué)得話,你也是可以使用 Executors 去創(chuàng)建的。
你還知道有其他方式去創(chuàng)建線程池的么?
網(wǎng)站欄目:阿里規(guī)范竟然不讓我用這種方式創(chuàng)建線程池
瀏覽路徑:http://fisionsoft.com.cn/article/dppgidc.html


咨詢
建站咨詢
