国产成人精品久久免费动漫-国产成人精品天堂-国产成人精品区在线观看-国产成人精品日本-a级毛片无码免费真人-a级毛片毛片免费观看久潮喷

您的位置:首頁(yè)技術(shù)文章
文章詳情頁(yè)

深度源碼解析Java 線程池的實(shí)現(xiàn)原理

瀏覽:118日期:2022-08-11 17:35:29
目錄線程池的優(yōu)點(diǎn)線程池的實(shí)現(xiàn)原理ThreadPoolExecutor阻塞隊(duì)列線程池工廠拒絕策略提交任務(wù)到線程池execute 方法submit 方法關(guān)閉線程池合理的參數(shù)7、本文小結(jié)

java 系統(tǒng)的運(yùn)行歸根到底是程序的運(yùn)行,程序的運(yùn)行歸根到底是代碼的執(zhí)行,代碼的執(zhí)行歸根到底是虛擬機(jī)的執(zhí)行,虛擬機(jī)的執(zhí)行其實(shí)就是操作系統(tǒng)的線程在執(zhí)行,并且會(huì)占用一定的系統(tǒng)資源,如CPU、內(nèi)存、磁盤(pán)、網(wǎng)絡(luò)等等。所以,如何高效的使用這些資源就是程序員在平時(shí)寫(xiě)代碼時(shí)候的一個(gè)努力的方向。本文要說(shuō)的線程池就是一種對(duì) CPU 利用的優(yōu)化手段。

線程池,百度百科是這么解釋的:

線程池是一種多線程處理形式,處理過(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)。

線程池,其實(shí)就是維護(hù)了很多線程的池子,類似這樣的技術(shù)還有很多的,例如:HttpClient 連接池、數(shù)據(jù)庫(kù)連接池、內(nèi)存池等等。

線程池的優(yōu)點(diǎn)

在 Java 并發(fā)編程框架中的線程池是運(yùn)用場(chǎng)景最多的技術(shù),幾乎所有需要異步或并發(fā)執(zhí)行任務(wù)的程序都可以使用線程池。在開(kāi)發(fā)過(guò)程中,合理地使用線程池能夠帶來(lái)至少以下4個(gè)好處。

第一:降低資源消耗。通過(guò)重復(fù)利用已創(chuàng)建的線程降低線程創(chuàng)建和銷毀造成的消耗;

第二:提高響應(yīng)速度。當(dāng)任務(wù)到達(dá)時(shí),任務(wù)可以不需要等到線程創(chuàng)建就能立即執(zhí)行;

第三:提高線程的可管理性。線程是稀缺資源,如果無(wú)限制地創(chuàng)建,不僅會(huì)消耗系統(tǒng)資源,還會(huì)降低系統(tǒng)的穩(wěn)定性,使用線程池可以進(jìn)行統(tǒng)一分配、調(diào)優(yōu)和監(jiān)控。

第四:提供更強(qiáng)大的功能,比如延時(shí)定時(shí)線程池;

線程池的實(shí)現(xiàn)原理

當(dāng)向線程池提交一個(gè)任務(wù)之后,線程池是如何處理這個(gè)任務(wù)的呢?下面就先來(lái)看一下它的主要處理流程。先來(lái)看下面的這張圖,然后我們一步一步的來(lái)解釋。

深度源碼解析Java 線程池的實(shí)現(xiàn)原理

當(dāng)使用者將一個(gè)任務(wù)提交到線程池以后,線程池是這么執(zhí)行的:

①首先判斷核心的線程數(shù)是否已滿,如果沒(méi)有滿,那么就去創(chuàng)建一個(gè)線程去執(zhí)行該任務(wù);否則請(qǐng)看下一步

②如果線程池的核心線程數(shù)已滿,那么就繼續(xù)判斷任務(wù)隊(duì)列是否已滿,如果沒(méi)滿,那么就將任務(wù)放到任務(wù)隊(duì)列中;否則請(qǐng)看下一步

③如果任務(wù)隊(duì)列已滿,那么就判斷線程池是否已滿,如果沒(méi)滿,那么就創(chuàng)建線程去執(zhí)行該任務(wù);否則請(qǐng)看下一步;

④如果線程池已滿,那么就根據(jù)拒絕策略來(lái)做出相應(yīng)的處理;

上面的四步其實(shí)就已經(jīng)將線程池的執(zhí)行原理描述結(jié)束了。如果不明白沒(méi)有關(guān)系,先一步一步往下看,上面涉及到的線程池的專有名詞都會(huì)詳細(xì)的介紹到。

我們?cè)谄綍r(shí)的開(kāi)發(fā)中,線程池的使用基本都是基于ThreadPoolExexutor類,他的繼承體系是這樣子的:

深度源碼解析Java 線程池的實(shí)現(xiàn)原理

那既然說(shuō)在使用中都是基于ThreadPoolExecutor的那么我們就重點(diǎn)分析這個(gè)類。

至于他構(gòu)造體系中的其他的類或者是接口中的屬性,這里就不去截圖了,完全沒(méi)有必要。小伙伴如果實(shí)在想看就自己去打開(kāi)代碼看一下就行了。

ThreadPoolExecutor

在《阿里巴巴 java 開(kāi)發(fā)手冊(cè)》中指出了線程資源必須通過(guò)線程池提供,不允許在應(yīng)用中自行顯示的創(chuàng)建線程,這樣一方面是線程的創(chuàng)建更加規(guī)范,可以合理控制開(kāi)辟線程的數(shù)量;另一方面線程的細(xì)節(jié)管理交給線程池處理,優(yōu)化了資源的開(kāi)銷。

其原文描述如下:

深度源碼解析Java 線程池的實(shí)現(xiàn)原理

在ThreadPoolExecutor類中提供了四個(gè)構(gòu)造方法,但是他的四個(gè)構(gòu)造器中,實(shí)際上最終都會(huì)調(diào)用同一個(gè)構(gòu)造器,只不過(guò)是在另外三個(gè)構(gòu)造器中,如果有些參數(shù)不傳ThreadPoolExecutor會(huì)幫你使用默認(rèn)的參數(shù)。所以,我們直接來(lái)看這個(gè)完整參數(shù)的構(gòu)造器,來(lái)徹底剖析里面的參數(shù)。

public class ThreadPoolExecutor extends AbstractExecutorService { ...... public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize,long keepAliveTime,TimeUnit unit, BlockingQueue<Runnable> workQueue,ThreadFactory threadFactory, RejectedExecutionHandler handler) { if (corePoolSize < 0 || maximumPoolSize <= 0 || maximumPoolSize < corePoolSize || keepAliveTime < 0){throw new IllegalArgumentException(); } if (workQueue == null || threadFactory == null || handler == null){throw new NullPointerException(); } this.acc = System.getSecurityManager() == null ? null : AccessController.getContext(); this.corePoolSize = corePoolSize; this.maximumPoolSize = maximumPoolSize; this.workQueue = workQueue; this.keepAliveTime = unit.toNanos(keepAliveTime); this.threadFactory = threadFactory; this.handler = handler;}}

主要參數(shù)就是下面這幾個(gè):

corePoolSize:線程池中的核心線程數(shù),包括空閑線程,也就是核心線程數(shù)的大小; maximumPoolSize:線程池中允許的最多的線程數(shù),也就是說(shuō)線程池中的線程數(shù)是不可能超過(guò)該值的; keepAliveTime:當(dāng)線程池中的線程數(shù)大于 corePoolSize 的時(shí)候,在超過(guò)指定的時(shí)間之后就會(huì)將多出 corePoolSize 的的空閑的線程從線程池中刪除; unit:keepAliveTime 參數(shù)的單位(常用的秒為單位); workQueue:用于保存任務(wù)的隊(duì)列,此隊(duì)列僅保持由 executor 方法提交的任務(wù) Runnable 任務(wù); threadFactory:線程池工廠,他主要是為了給線程起一個(gè)標(biāo)識(shí)。也就是為線程起一個(gè)具有意義的名稱; handler:拒絕策略阻塞隊(duì)列

workQueue 有多種選擇,在 JDK 中一共提供了 7 中阻塞對(duì)列,分別為:

ArrayBlockingQueue : 一個(gè)由數(shù)組結(jié)構(gòu)組成的有界阻塞隊(duì)列。 此隊(duì)列按照先進(jìn)先出(FIFO)的原則對(duì)元素進(jìn)行排序。默認(rèn)情況下不保證訪問(wèn)者公平地訪問(wèn)隊(duì)列 ,所謂公平訪問(wèn)隊(duì)列是指阻塞的線程,可按照阻塞的先后順序訪問(wèn)隊(duì)列。非公平性是對(duì)先等待的線程是不公平的,當(dāng)隊(duì)列可用時(shí),阻塞的線程都可以競(jìng)爭(zhēng)訪問(wèn)隊(duì)列的資格。 LinkedBlockingQueue : 一個(gè)由鏈表結(jié)構(gòu)組成的有界阻塞隊(duì)列。 此隊(duì)列的默認(rèn)和最大長(zhǎng)度為Integer.MAX_VALUE。 此隊(duì)列按照先進(jìn)先出的原則對(duì)元素進(jìn)行排序。 PriorityBlockingQueue : 一個(gè)支持優(yōu)先級(jí)排序的無(wú)界阻塞隊(duì)列。 (雖然此隊(duì)列邏輯上是無(wú)界的,但是資源被耗盡時(shí)試圖執(zhí)行 add 操作也將失敗,導(dǎo)致 OutOfMemoryError) DelayQueue: 一個(gè)使用優(yōu)先級(jí)隊(duì)列實(shí)現(xiàn)的無(wú)界阻塞隊(duì)列。 元素的一個(gè)無(wú)界阻塞隊(duì)列,只有在延遲期滿時(shí)才能從中提取元素 SynchronousQueue: 一個(gè)不存儲(chǔ)元素的阻塞隊(duì)列。 一種阻塞隊(duì)列,其中每個(gè)插入操作必須等待另一個(gè)線程的對(duì)應(yīng)移除操作 ,反之亦然。(SynchronousQueue 該隊(duì)列不保存元素) LinkedTransferQueue: 一個(gè)由鏈表結(jié)構(gòu)組成的無(wú)界阻塞隊(duì)列。 相對(duì)于其他阻塞隊(duì)列LinkedTransferQueue多了tryTransfer和transfer方法。 LinkedBlockingDeque: 一個(gè)由鏈表結(jié)構(gòu)組成的雙向阻塞隊(duì)列。 是一個(gè)由鏈表結(jié)構(gòu)組成的雙向阻塞隊(duì)列

在以上的7個(gè)隊(duì)列中,線程池中常用的是ArrayBlockingQueue、LinkedBlockingQueue、SynchronousQueue,

隊(duì)列中的常用的方法如下:

類型 方法 含義 特點(diǎn) 拋異常 add 添加一個(gè)元素 如果隊(duì)列滿,拋出異常 IllegalStateException 拋異常 remove 返回并刪除隊(duì)列的頭節(jié)點(diǎn) 如果隊(duì)列空,拋出異常 NoSuchElementException 拋異常 element 返回隊(duì)列頭節(jié)點(diǎn) 如果隊(duì)列空,拋出異常 NoSuchElementException 不拋異常,但是不阻塞 offer 添加一個(gè)元素 添加成功,返回 true,添加失敗,返回 false 不拋異常,但是不阻塞 poll 返回并刪除隊(duì)列的頭節(jié)點(diǎn) 如果隊(duì)列空,返回 null 不拋異常,但是不阻塞 peek 返回隊(duì)列頭節(jié)點(diǎn) 如果隊(duì)列空,返回 null 阻塞 put 添加一個(gè)元素 如果隊(duì)列滿,阻塞 阻塞 take 返回并刪除隊(duì)列的頭節(jié)點(diǎn) 如果隊(duì)列空,阻塞

關(guān)于阻塞隊(duì)列,介紹到這里也就基本差不多了。

線程池工廠

線程池工廠,就像上面已經(jīng)介紹的,目的是為了給線程起一個(gè)有意義的名字。用起來(lái)也非常的簡(jiǎn)單,只需要實(shí)現(xiàn)ThreadFactory接口即可

public class CustomThreadFactory implements ThreadFactory { @Override public Thread newThread(Runnable r) {Thread thread = new Thread(r);thread.setName('我是你們自己定義的線程名稱');return thread; }}

具體的使用就不去廢話了。

拒絕策略

線程池有四種默認(rèn)的拒絕策略,分別為:

AbortPolicy:這是線程池默認(rèn)的拒絕策略,在任務(wù)不能再提交的時(shí)候,拋出異常,及時(shí)反饋程序運(yùn)行狀態(tài)。如果是比較關(guān)鍵的業(yè)務(wù),推薦使用此拒絕策略,這樣子在系統(tǒng)不能承載更大的并發(fā)量的時(shí)候,能夠及時(shí)的通過(guò)異常發(fā)現(xiàn); DiscardPolicy:丟棄任務(wù),但是不拋出異常。如果線程隊(duì)列已滿,則后續(xù)提交的任務(wù)都會(huì)被丟棄,且是靜默丟棄。這玩意不建議使用; DiscardOldestPolicy:丟棄隊(duì)列最前面的任務(wù),然后重新提交被拒絕的任務(wù)。這玩意不建議使用; CallerRunsPolicy:如果任務(wù)添加失敗,那么主線程就會(huì)自己調(diào)用執(zhí)行器中的 executor 方法來(lái)執(zhí)行該任務(wù)。這玩意不建議使用;

也就是說(shuō)關(guān)于線程池的拒絕策略,最好使用默認(rèn)的。這樣能夠及時(shí)發(fā)現(xiàn)異常。如果上面的都不能滿足你的需求,你也可以自定義拒絕策略,只需要實(shí)現(xiàn) RejectedExecutionHandler 接口即可

public class CustomRejection implements RejectedExecutionHandler { @Override public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {System.out.println('你自己想怎么處理就怎么處理'); }}

看到這里,我們?cè)賮?lái)畫(huà)一張圖來(lái)總結(jié)和概括下線程池的執(zhí)行示意圖:

深度源碼解析Java 線程池的實(shí)現(xiàn)原理

詳細(xì)的執(zhí)行過(guò)程全部在圖中說(shuō)明了。

提交任務(wù)到線程池

在 java 中,有兩個(gè)方法可以將任務(wù)提交到線程池,分別是submit和execute。

execute 方法

execute()方法用于提交不需要返回值的任務(wù),所以無(wú)法判斷任務(wù)是否被線程池執(zhí)行成功。

void execute(Runnable command);

通過(guò)以下代碼可知 execute() 方法輸入的任務(wù)是一個(gè)Runnable類的實(shí)例。

executorService.execute(()->{ System.out.println('ThreadPoolDemo.execute');});submit 方法

submit()方法用于提交需要返回值的任務(wù)。

Future<?> submit(Runnable task);

線程池會(huì)返回一個(gè)future類型的對(duì)象,通過(guò)這個(gè) future 對(duì)象可以判斷任務(wù)是否執(zhí)行成功,并且可以通過(guò)future的get()方法來(lái)獲取返回值,get() 方法會(huì)阻塞當(dāng)前線程直到任務(wù)完成,而使用get(long timeout,TimeUnit unit)方法則會(huì)阻塞當(dāng)前線程一段時(shí)間后立即返回,這時(shí)候有可能任務(wù)沒(méi)有執(zhí)行完。

Future<?> submit = executorService.submit(() -> { System.out.println('ThreadPoolDemo.submit');});關(guān)閉線程池

其實(shí),如果優(yōu)雅的關(guān)閉線程池是一個(gè)令人頭疼的問(wèn)題,線程開(kāi)啟是簡(jiǎn)單的,但是想要停止卻不是那么容易的。通常而言, 大部分程序員都是使用 jdk 提供的兩個(gè)方法來(lái)關(guān)閉線程池,他們分別是:shutdown 或 shutdownNow;

通過(guò)調(diào)用線程池的 shutdown 或 shutdownNow 方法來(lái)關(guān)閉線程池。它們的原理是遍歷線程池中的工作線程,然后逐個(gè)調(diào)用線程的 interrupt 方法來(lái)中斷線程(PS:中斷,僅僅是給線程打上一個(gè)標(biāo)記,并不是代表這個(gè)線程停止了,如果線程不響應(yīng)中斷,那么這個(gè)標(biāo)記將毫無(wú)作用),所以無(wú)法響應(yīng)中斷的任務(wù)可能永遠(yuǎn)無(wú)法終止。

但是它們存在一定的區(qū)別,shutdownNow首先將線程池的狀態(tài)設(shè)置成 STOP,然后嘗試停止所有的正在執(zhí)行或暫停任務(wù)的線程,并返回等待執(zhí)行任務(wù)的列表,而 shutdown 只是將線程池的狀態(tài)設(shè)置成SHUTDOWN狀態(tài),然后中斷所有沒(méi)有正在執(zhí)行任務(wù)的線程。

只要調(diào)用了這兩個(gè)關(guān)閉方法中的任意一個(gè),isShutdown 方法就會(huì)返回 true。當(dāng)所有的任務(wù)都已關(guān)閉后,才表示線程池關(guān)閉成功,這時(shí)調(diào)用isTerminaed方法會(huì)返回 true。至于應(yīng)該調(diào)用哪一種方法來(lái)關(guān)閉線程池,應(yīng)該由提交到線程池的任務(wù)特性決定,通常調(diào)用 shutdown方法來(lái)關(guān)閉線程池,如果任務(wù)不一定要執(zhí)行完,則可以調(diào)用 shutdownNow 方法。

這里推薦使用穩(wěn)妥的 shutdownNow 來(lái)關(guān)閉線程池,至于更優(yōu)雅的方式我會(huì)在以后的并發(fā)編程設(shè)計(jì)模式中的兩階段終止模式中會(huì)再次詳細(xì)介紹。

合理的參數(shù)

為什么叫合理的參數(shù),那不合理的參數(shù)是什么樣子的?在我們創(chuàng)建線程池的時(shí)候,里面的參數(shù)該如何設(shè)置才能稱之為合理呢?其實(shí)這是有一定的依據(jù)的,我們先來(lái)看一下以下的創(chuàng)建的方式:

ExecutorService executorService = new ThreadPoolExecutor(5,5,5,TimeUnit.SECONDS,new ArrayBlockingQueue<>(5),r -> { Thread thread = new Thread(r); thread.setName('線程池原理講解'); return thread;});

你說(shuō)他合理不合理?我也不知道,因?yàn)槲覀儧](méi)有參考的依據(jù),在實(shí)際的開(kāi)發(fā)中,我們需要根據(jù)任務(wù)的性質(zhì)(IO是否頻繁?)來(lái)決定我們創(chuàng)建的核心的線程數(shù)的大小,實(shí)際上可以從以下的一個(gè)角度來(lái)分析:

任務(wù)的性質(zhì):CPU密集型任務(wù)、IO密集型任務(wù)和混合型任務(wù); 任務(wù)的優(yōu)先級(jí):高、中和低; 任務(wù)的執(zhí)行時(shí)間:長(zhǎng)、中和短; 任務(wù)的依賴性:是否依賴其他系統(tǒng)資源,如數(shù)據(jù)庫(kù)連接;

性質(zhì)不同的任務(wù)可以用不同規(guī)模的線程池分開(kāi)處理。分為CPU密集型和IO密集型。

CPU密集型任務(wù)應(yīng)配置盡可能小的線程,如配置 Ncpu+1個(gè)線程的線程池。(可以通過(guò)Runtime.getRuntime().availableProcessors()來(lái)獲取CPU物理核數(shù))

IO密集型任務(wù)線程并不是一直在執(zhí)行任務(wù),則應(yīng)配置盡可能多的線程,如 2*Ncpu。

混合型的任務(wù),如果可以拆分,將其拆分成一個(gè)CPU密集型任務(wù)一個(gè)IO密集型任務(wù),只要這兩個(gè)任務(wù)執(zhí)行的時(shí)間相差不是太大,那么分解后執(zhí)行的吞吐量將高于串行執(zhí)行的吞吐量。

如果這兩個(gè)任務(wù)執(zhí)行時(shí)間相差太大,則沒(méi)必要進(jìn)行分解。可以通過(guò) Runtime.getRuntime().availableProcessors() 方法獲得當(dāng)前設(shè)備的CPU個(gè)數(shù)。

優(yōu)先級(jí)不同的任務(wù)可以使用優(yōu)先級(jí)隊(duì)列 PriorityBlockingQueue來(lái)處理。它可以讓優(yōu)先級(jí)高的任務(wù)先執(zhí)行(注意:如果一直有優(yōu)先級(jí)高的任務(wù)提交到隊(duì)列里,那么優(yōu)先級(jí)低的任務(wù)可能永遠(yuǎn)不能執(zhí)行)

執(zhí)行時(shí)間不同的任務(wù)可以交給不同規(guī)模的線程池來(lái)處理,或者可以使用優(yōu)先級(jí)隊(duì)列,讓執(zhí)行時(shí)間短的任務(wù)先執(zhí)行。依賴數(shù)據(jù)庫(kù)連接池的任務(wù),因?yàn)榫€程提交SQL后需要等待數(shù)據(jù)庫(kù)返回結(jié)果,等待的時(shí)間越長(zhǎng),則 CPU 空閑時(shí)間就越長(zhǎng),那么線程數(shù)應(yīng)該設(shè)置得越大,這樣才能更好地利用CPU。

建議使用有界隊(duì)列。有界隊(duì)列能增加系統(tǒng)的穩(wěn)定性和預(yù)警能力,可以根據(jù)需要設(shè)大一點(diǎn)。方式因?yàn)樘峤坏娜蝿?wù)過(guò)多而導(dǎo)致 OOM;

7、本文小結(jié)

本文主要介紹的是線程池的實(shí)現(xiàn)原理以及一些使用技巧,在實(shí)際開(kāi)發(fā)中,線程池可以說(shuō)是稍微高級(jí)一點(diǎn)的程序員的必備技能。所以掌握好線程池這門技術(shù)也是重中之重!

以上就是深度源碼解析Java 線程池的實(shí)現(xiàn)原理的詳細(xì)內(nèi)容,更多關(guān)于Java 線程池的資料請(qǐng)關(guān)注好吧啦網(wǎng)其它相關(guān)文章!

標(biāo)簽: Java
相關(guān)文章:
主站蜘蛛池模板: 日本a级三级三级三级久久 日本a级特黄三级三级三级 | 韩国免费网站成人 | 日韩不卡一区二区三区 | 欧美理论大片清免费观看 | 欧美亚洲在线观看 | 亚洲视频免费在线观看 | 日本一区二区三区不卡视频中文字幕 | 中文一区在线 | 日本国产最新一区二区三区 | 男女在线免费视频 | 大狠狠大臿蕉香蕉大视频 | 91男女视频 | 中文字幕在线免费观看视频 | www.日本高清视频.com | 人成精品视频三区二区一区 | 黄色免费在线网址 | 国产97在线观看 | 美国美女一级毛片免费全 | 黄色三级网站免费 | 亚洲影院手机版777点击进入影院 | 91精品国产免费久久久久久 | 成人18免费网站在线观看 | 男人和女人的做刺激性视频 | 欧美成人观看免费版 | 国产午夜毛片v一区二区三区 | 特及毛片| 亚洲欧美另类日本久久影院 | 手机看片免费基地 | 日本在线网 | 国内真实愉拍系列情侣自拍 | 亚洲欧美日韩精品久久 | 一区二区三区免费观看 | 亚洲天天在线 | 国产一级生活片 | 欧美黄网站 | 欧美精品久久久久久久免费观看 | 国内成人精品亚洲日本语音 | 国产手机精品视频 | 亚洲天堂在线观看视频 | 手机看片日韩日韩国产在线看 | 欧美 亚洲 中文字幕 |