目錄
??今日良言:不悲傷 不彷徨 有風(fēng)聽風(fēng) 有雨看雨
??一、簡介
??二、相關(guān)代碼
??1.線程池代碼
??2.自定義實(shí)現(xiàn)線程池
??三、ThreadPoolExecutor類
??今日良言:不悲傷 不彷徨 有風(fēng)聽風(fēng) 有雨看雨
??一、簡介
首先來介紹一下什么是線程池,線程池是一種利用池化技術(shù)思想來實(shí)現(xiàn)的線程管理技術(shù),主要是為了復(fù)用線程、便利地管理線程和任務(wù)并將線程的創(chuàng)建和任務(wù)的執(zhí)行解耦開來。我們可以創(chuàng)建線程池來復(fù)用已經(jīng)創(chuàng)建的線程來降低頻繁創(chuàng)建和銷毀線程所帶來的資源消耗。在JAVA中主要是通過java.util.concurrent包中的ThreadPoolExecutor類來實(shí)現(xiàn)線程池 。
??二、相關(guān)代碼
??1.線程池代碼
這里就是創(chuàng)造出一個(gè)10個(gè)線程的線程池,然后就可以隨機(jī)安排這些線程完成任務(wù)了。
線程池提供了一個(gè)重要的方法 submit 可以給線程池提交若干個(gè)任務(wù)。
?submit的參數(shù)是一個(gè)Runnable,用來描述這些線程要執(zhí)行的任務(wù)是什么。
線程池中的線程都是前臺(tái)線程,前臺(tái)線程會(huì)阻止進(jìn)程結(jié)束,也就是說,運(yùn)行程序之后,main線程結(jié)束了,但是整個(gè)進(jìn)程沒有結(jié)束。
當(dāng)前是往線程池里放了 1000 個(gè)任務(wù),1000 個(gè)任務(wù)就是由這 10 個(gè)線程來平均分配一下,差不多是一人執(zhí)行 100個(gè),但是這里并非是嚴(yán)格的平均,可能有的多一個(gè)有的少一個(gè)都正常。(每個(gè)線程都執(zhí)行完一個(gè)任務(wù)之后,再立即取下一個(gè)任務(wù)...由于每個(gè)任務(wù)執(zhí)行時(shí)間都差不多,因此每個(gè)線程做的任務(wù)數(shù)量就差不多)
上述代碼涉及到變量捕獲
?這里的 i 是主線程里的局部變量(在主線程的棧上),隨著主線程的代碼執(zhí)行結(jié)束就銷毀了,但是,很可能這里的 for 循環(huán)已經(jīng)執(zhí)行完了,當(dāng)前 run 的任務(wù)在線程池里還沒有排到,此時(shí) i 就已經(jīng)要銷毀了。這里的 run 方法屬于 Runnable ,這個(gè)方法的執(zhí)行時(shí)機(jī)并不是立刻執(zhí)行,而是在未來的某個(gè)時(shí)間點(diǎn)(后續(xù)在線程池的隊(duì)列中排到了) 才會(huì)執(zhí)行,為了避免作用域的差異,導(dǎo)致執(zhí)行 run 方法的時(shí)候,i 已經(jīng)銷毀了,于是就有了變量捕獲,也就是讓 run 方法把主線程的 i 往當(dāng)前 run 方法的棧上拷貝一份(在定義 run 方法的時(shí)候了,把當(dāng)前 i 的值記住,后續(xù)執(zhí)行 run 的時(shí)候,就創(chuàng)建一個(gè)也叫做 i 的局部變量,并且把這個(gè)值給賦值過去)。
在Java 中,對(duì)于變量捕獲,做了一些額外的要求,在JDK 1.8之前,要求變量捕獲只能捕獲 final 修飾的變量,后來發(fā)現(xiàn),這樣太麻煩了,于是,在 JDK 1.8 開始,發(fā)送了一點(diǎn)標(biāo)準(zhǔn),要求不一定非得待 final 關(guān)鍵字,只要代碼中沒有修改過這個(gè)變量,也可以捕獲。
在上述代碼中,i 是被修改的,因此不能捕獲,但是 n 沒有被修改,所以可以被捕獲。
接下來,介紹一下幾種不同的線程池:
new FixedThreadPool? ? ? ? 創(chuàng)建固定線程數(shù)的線程池。
newCachedThreadPool? ? ? 線程數(shù)量是動(dòng)態(tài)變化的,任務(wù)多了就多創(chuàng)建幾個(gè)線程,任務(wù)少了?
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 就少創(chuàng)建幾個(gè)。
newScheduledThreadPool? 類似于定時(shí)器,讓任務(wù)延時(shí)執(zhí)行。
newSingleThreadExecutor? 線程池里只有一個(gè)線程。
上述這些線程池,本質(zhì)上都是通過包裝 ThreadPoolExecutor 來實(shí)現(xiàn)的,這個(gè)線程池用起來比較麻煩,所以提供了工廠類,讓我們使用更方便,ThreadPoolExecutor 提供的功能更為強(qiáng)大,后面會(huì)詳細(xì)介紹。?
??2.自定義實(shí)現(xiàn)線程池
?自定義實(shí)現(xiàn)線程池代碼如下:
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
class MyThreadPool {
// 阻塞隊(duì)列
private BlockingQueue<Runnable> queue = new LinkedBlockingQueue<>();
// 若干個(gè)工作線程
// n表示線程的數(shù)量
public MyThreadPool(int n) {
for (int i = 0; i < n; i++) {
Thread t = new Thread(() -> {
while (true) {
try {
// 從阻塞隊(duì)列中取出然后執(zhí)行
Runnable runnable = queue.take();
runnable.run();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
});
t.start();
}
}
// 注冊(cè)任務(wù)給線程池
public void submit(Runnable runnable) {
try {
queue.put(runnable);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
public class Exercise{
public static void main(String[] args) {
MyThreadPool myThreadPool = new MyThreadPool(10);
for (int i = 0; i < 1000; i++) {
int n = i;
myThreadPool.submit(new Runnable() {
@Override
public void run() {
System.out.println("hello:"+n);
}
});
}
}
}
??三、ThreadPoolExecutor類
最后,介紹一下ThreadPoolExecutor 里面的參數(shù),如下圖:
1)int corePoolSize
核心線程數(shù)
2)int maximumPoolSize
最大線程數(shù)
ThreadPoolExecutor 相當(dāng)于是把里面的線程分成了兩類,一類是核心線程,一類是臨時(shí)線程,核心線程相當(dāng)于是正式工,臨時(shí)線程相當(dāng)于是臨時(shí)工,這二者之和就是最大線程數(shù)。
如果任務(wù)多,需要?jiǎng)?chuàng)建更多的線程,但是,一個(gè)程序的任務(wù)不一定始終都有很多,有時(shí)候多,有時(shí)候少,如果現(xiàn)在任務(wù)少了,線程還那么多,就非常不合適了,因此,就需要對(duì)現(xiàn)有的線程進(jìn)行一定的淘汰,整體的淘汰策略是:核心線程保底,臨時(shí)線程動(dòng)態(tài)調(diào)節(jié)。
在實(shí)際開發(fā)的時(shí)候,線程池的線程數(shù)設(shè)置成多少比較合適呢?
實(shí)際上,這里不應(yīng)該回答出具體的數(shù)字,在實(shí)際開發(fā)中,線程池的線程數(shù)設(shè)置需要根據(jù)具體情況進(jìn)行調(diào)整,一般來說,應(yīng)該設(shè)置為CPU核心數(shù)的兩倍到四倍之間,如果線程數(shù)過少,則導(dǎo)致任務(wù)等待時(shí)間過長,而如果線程數(shù)過多,會(huì)導(dǎo)致系統(tǒng)資源浪費(fèi)。如果任務(wù)是IO密集型的,那么可以適當(dāng)?shù)脑黾泳€程數(shù),如果任務(wù)是CPU密集型的,則可以適當(dāng)?shù)臏p少線程數(shù)。
3)long?keepAliveTime
臨時(shí)線程的最大空閑時(shí)間,超出這個(gè)時(shí)間,臨時(shí)線程就會(huì)被銷毀了。
也就是臨時(shí)工的最大摸魚時(shí)間。
4)TimeUnit unit?
時(shí)間單位(s,ms,分鐘...)
5)BlockingQueue<Runnable>?workQueue
線程池的任務(wù)隊(duì)列
此處使用的是阻塞隊(duì)列,每個(gè)線程都是在不停的嘗試take,如果有任務(wù),就take成功,沒有就阻塞。
6)ThreadFactory? threadFactory
用于創(chuàng)建線程,線程池是需要?jiǎng)?chuàng)建線程的
7)RejectedExecutionHandler handler
描述了線程池的“拒絕策略”,也是一個(gè)特殊的對(duì)象,描述了當(dāng)線程池任務(wù)隊(duì)列滿了,如果繼續(xù)添加任務(wù)會(huì)有什么樣的行為。
標(biāo)準(zhǔn)庫提供了四個(gè)拒絕策略,如下圖:
?第一個(gè)拒絕策略
如果任務(wù)太多,任務(wù)隊(duì)列滿了,就直接拋出異常。
第二個(gè)拒絕策略
如果任務(wù)太多,任務(wù)隊(duì)列滿了,多出來的任務(wù),誰加的誰負(fù)責(zé)執(zhí)行。
第三個(gè)拒絕策略
如果任務(wù)太多,任務(wù)隊(duì)列滿了,丟棄最早的任務(wù)。
第四個(gè)拒絕策略
如果任務(wù)太多,任務(wù)隊(duì)列滿了,丟棄最新的任務(wù)。文章來源:http://www.zghlxwxcb.cn/news/detail-663671.html
?以上就是本篇博客的所有內(nèi)容了,望有所幫助~文章來源地址http://www.zghlxwxcb.cn/news/detail-663671.html
到了這里,關(guān)于Java并發(fā)編程之線程池詳解的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!