新鄉(xiāng)營(yíng)銷型網(wǎng)站建設(shè)產(chǎn)品營(yíng)銷推廣策略
目錄
一.什么是線程池
二.線程池的實(shí)現(xiàn)原理
🎈為什么要有工廠模式?
三.線程池的構(gòu)造方法解讀
🎈線程池的拒絕策略
四.自己實(shí)現(xiàn)一個(gè)線程池
一.什么是線程池
簡(jiǎn)單來說,線程池就好比一塊魚塘,魚塘中的每條魚就是一個(gè)線程。那么為什么要有這個(gè)線程池呢?就好比 一個(gè)“渣女\渣男”,當(dāng)他和A在一起的時(shí)候,如果想和B在一起,那么就需要先想辦法和A分手,再和B搞好關(guān)系,最終和B在一起。如果她和A談的時(shí)候,已經(jīng)找好了B C D,此時(shí)就可以直接拿來無縫銜接~~
其實(shí)線程池也就大概這個(gè)作用,里面存放一些線程,需要用的時(shí)候直接拿來使用。
二.線程池的實(shí)現(xiàn)原理
我們先來看線程池是如何創(chuàng)建的:
package Pool;import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;public class threadPoolDemo {public static void main(String[] args) {ExecutorService executor = Executors.newSingleThreadExecutor();}
}
很明顯,此處的線程池竟然不是 new 出來的,那么它是如何被實(shí)例化的呢?其實(shí)這里就使用了一種設(shè)計(jì)模式:工廠模式。
🎈為什么要有工廠模式?
其實(shí)工廠模式就時(shí)給Java中的構(gòu)造方法填坑的,我們的構(gòu)造方法其實(shí)時(shí)有很大缺陷的,我們來看以下例子。
我們期望這個(gè)Point類,初始化的時(shí)候能夠傳入 double x,double y,用來構(gòu)造笛卡爾坐標(biāo)系
我們又希望在不創(chuàng)建其他類的情況下,這個(gè)Point類,?初始化的時(shí)候能夠傳入 double r,double a,用來構(gòu)造極坐標(biāo)系。
但是構(gòu)造方法也是方法,此時(shí)的方法由于參數(shù)個(gè)數(shù)和參數(shù)的類型都相同,就會(huì)編譯失敗,那么就沒辦法滿足期望!
此時(shí)我們可以再寫一個(gè)類:
此時(shí)這個(gè)類中又兩個(gè)靜態(tài)方法,一個(gè)是構(gòu)造笛卡爾坐標(biāo)系,并且返回。一個(gè)是構(gòu)造及坐標(biāo)系,并且返回對(duì)象。
那么我們就可以使用以下語句來分別調(diào)用:
Point p1 = PointFactory.makePintByXY(10,20)?
Point p2?= PointFactory.makePintByRA(12,63)?
此時(shí)通過PointFactory類,來給Point類傳入需要的值就可以了。
那么線程池也是通過這樣的方式來進(jìn)行創(chuàng)建的:
三.線程池的構(gòu)造方法解讀
從Excutor這個(gè)工廠類的源碼中可以得到以下:
其實(shí)線程的創(chuàng)建又被封裝到了一個(gè)叫做ThreadPoolExecutor的類中
點(diǎn)開ThreadPoolExecutor,可以得到如下圖片:
其中的每個(gè)參數(shù)的意思是這樣的:
第一個(gè)是核心線程數(shù), 第二個(gè)是最大線程數(shù)
線程池中的線程數(shù)目是可以動(dòng)態(tài)變化的
范圍就是【int corPoolSize. ~ int maxmumPoolSize】
什么是核心線程:
就好比一個(gè)公司中有正式員工(核心線程)和實(shí)習(xí)生,總的員工數(shù)目不能超過一定的值。當(dāng)人手不夠用就招實(shí)習(xí)生,這樣既可以滿足效率的需求,又可以避免過多的開銷。
第三個(gè)是線程的可存活時(shí)間
第四個(gè)TimeUnit unit是用來設(shè)置非核心線程閑置超時(shí)時(shí)長(zhǎng)(keepAliveTime)的單位。當(dāng)一個(gè)非核心線程的閑置時(shí)間超過這個(gè)參數(shù)所設(shè)定的時(shí)長(zhǎng)時(shí),該線程就會(huì)被銷毀掉。
第五個(gè)比較重要
第六個(gè)是線程池的拒絕策略,也就是當(dāng)所有線程都處于忙碌狀態(tài),如果還往線程池中添加元素,線程池所做的操作。
這些構(gòu)造方法,第一個(gè)和第五個(gè)以及第六個(gè)是需要重點(diǎn)掌握的。?
下面來單獨(dú)講講第六個(gè)參數(shù):拒絕策略
🎈線程池的拒絕策略
所謂的拒絕策略,其實(shí)就是如果線程池中每個(gè)線程都是處于忙碌的狀態(tài),如何應(yīng)對(duì)新來的線程任務(wù)。
舉個(gè)例子:如果我周一到周五都是滿課,此時(shí)我一朋友讓我給他去代課,那么此時(shí)我如何應(yīng)對(duì)?此時(shí)就會(huì)有相應(yīng)的應(yīng)對(duì)策略:
- AbortPolicy(默認(rèn)策略):這是默認(rèn)的拒絕策略,它會(huì)拋出一個(gè)未檢查的RejectedExecutionException,以指示任務(wù)被拒絕。也就是我本來都滿課了,朋友還讓我去代課,此時(shí)我就直接崩潰,代課和我自己的課我都不去上了,直接崩潰!
- CallerRunsPolicy:這個(gè)策略不會(huì)拋出異常。相反,它會(huì)將任務(wù)退回給調(diào)用線程,讓它自己運(yùn)行這個(gè)任務(wù)。 也就是讓我朋友自己去上課。
- DiscardOldestPolicy:此策略會(huì)丟棄隊(duì)列中等待最久的任務(wù),并立即返回給調(diào)用者。也就是我丟棄我課程中一節(jié)課,去給他代課。
- DiscardPolicy:這個(gè)策略會(huì)靜默地丟棄被拒絕的任務(wù)。也就是說,它不會(huì)拋出任何異常,也不會(huì)通知任務(wù)被拒絕。 也就是我拒絕去給他代課,我自己上自己的課。然后我朋友也不去上課了,那么這個(gè)課(任務(wù)) 也就黃了。
四.自己實(shí)現(xiàn)一個(gè)線程池
? 實(shí)現(xiàn)線程池一個(gè)最關(guān)鍵的步驟就是拒絕策略,那么說明拒絕策略呢?
由于之前學(xué)過阻塞隊(duì)列的知識(shí),這里就先用阻塞隊(duì)列來實(shí)現(xiàn)以下。
那么這個(gè)就是一種自己定義的新的拒絕策略,那就是一直等待~
package Pool;import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.BlockingQueue;class MyPoolDemo {//一個(gè)隊(duì)列BlockingQueue<Runnable> queue = new ArrayBlockingQueue<>(1000);//通過這個(gè)方法,把任務(wù)添加到隊(duì)列當(dāng)中public void submit(Runnable task) throws InterruptedException {queue.put(task); //往阻塞隊(duì)列中放入元素}public MyPoolDemo(int n) throws InterruptedException {//構(gòu)造方法for (int i = 0; i < n; i++) {Thread t = new Thread(() -> {//讓這個(gè)線程從隊(duì)列中消費(fèi)任務(wù)并且進(jìn)行執(zhí)行try {//如果隊(duì)列中沒有元素,那就阻塞等待//一旦隊(duì)列中有了任務(wù),那么就立即執(zhí)行take方法獲取到任務(wù)并且開始執(zhí)行Runnable task = task = queue.take();task.run();} catch (InterruptedException e) {throw new RuntimeException(e);}});t.start();}}} public class RunDemo {public static void main(String[] args) throws InterruptedException {/*** 步驟理解:* 1.創(chuàng)建線程池并且指定線程數(shù)目是 20 ,在實(shí)例化線程池的時(shí)候已經(jīng)創(chuàng)建好了20個(gè)線程* 2.這20個(gè)線程都在等待 take 獲取到任務(wù)隊(duì)列中的任務(wù)* 3.for 語句 循環(huán) 1000次,每次循環(huán)都會(huì)提交任務(wù)到任務(wù)隊(duì)列* 4.一但任務(wù)隊(duì)列里面有元素,這20個(gè)線程就會(huì)立馬獲取到,并且執(zhí)行*/MyPoolDemo myPoolDemo = new MyPoolDemo(20);int taskCount = 1000;while (true) {for (int i = 0; i < taskCount; i++) {int id = i;myPoolDemo.submit(new Runnable() {@Overridepublic void run() {System.out.println("執(zhí)行任務(wù):" + id);}});}}}}
運(yùn)行結(jié)果:
代碼解讀:
1.線程池里的線程是需要執(zhí)行任務(wù)的,這個(gè)任務(wù)可以放到? ? BlockingQueue 這個(gè)阻塞隊(duì)列中。為什么要使用阻塞隊(duì)列呢?當(dāng)線程池中的線程都在工作,此時(shí)就直接等待阻塞。
2.submit 方法接受一個(gè)實(shí)例化好的Runnable類型的任務(wù),負(fù)責(zé)往隊(duì)列中添加元素
3.?MyPoolDemo(int n) 是這個(gè)類的構(gòu)造放法,當(dāng)實(shí)例化這個(gè)類的時(shí)候,被指定的 n 就是要?jiǎng)?chuàng)建的線程數(shù)量。
4. 由運(yùn)行結(jié)果可以得出:當(dāng)線程數(shù)量為20的時(shí)候,可以看到任務(wù)被隨即執(zhí)行完了。
總結(jié):Java線程池是Java并發(fā)編程中一個(gè)重要的概念,它用于管理和控制線程的創(chuàng)建、銷毀,以及任務(wù)提交和執(zhí)行。線程池的主要目的是減少創(chuàng)建和銷毀線程的開銷,提高性能。?