日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

threadpoolexecutor底层实现原理

發(fā)布時(shí)間:2024/4/13 编程问答 33 豆豆
生活随笔 收集整理的這篇文章主要介紹了 threadpoolexecutor底层实现原理 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

線程池

什么是線程池

Java中的程池是運(yùn)用場(chǎng)景最多的并發(fā)框架,幾乎所有需要異步或并發(fā)執(zhí)行任務(wù)的程序
都可以使用程池。在開發(fā)過程中,合理地使用程池能夠帶3個(gè)好
第一:降低源消耗。通重復(fù)利用已創(chuàng)建的程降低創(chuàng)建和造成的消耗。
第二:提高響應(yīng)速度。當(dāng)任務(wù)到達(dá)時(shí),任務(wù)可以不需要等到創(chuàng)建就能立即執(zhí)行。
第三:提高程的可管理性程是稀缺源,如果無限制地創(chuàng)建,不會(huì)消耗系統(tǒng)資源,
會(huì)降低系統(tǒng)穩(wěn)定性,使用程池可以進(jìn)統(tǒng)一分配、調(diào)優(yōu)監(jiān)控。但是,要做到合理利用
程池,必須對(duì)實(shí)現(xiàn)原理了如指掌。

線程池作用

線程池是為突然大量爆發(fā)的線程設(shè)計(jì)的,通過有限的幾個(gè)固定線程為大量的操作服務(wù),減少了創(chuàng)建和銷毀線程所需的時(shí)間,從而提高效率。

如果一個(gè)線程的時(shí)間非常長(zhǎng),就沒必要用線程池了(不是不能作長(zhǎng)時(shí)間操作,而是不宜。),況且我們還不能控制線程池中線程的開始、掛起、和中止。

線程池的分類

ThreadPoolExecutor

Java是天生就支持并發(fā)的語言,支持并發(fā)意味著多線程,線程的頻繁創(chuàng)建在高并發(fā)及大數(shù)據(jù)量是非常消耗資源的,因?yàn)閖ava提供了線程池。在jdk1.5以前的版本中,線程池的使用是及其簡(jiǎn)陋的,但是在JDK1.5后,有了很大的改善。JDK1.5之后加入了java.util.concurrent包,java.util.concurrent包的加入給予開發(fā)人員開發(fā)并發(fā)程序以及解決并發(fā)問題很大的幫助。這篇文章主要介紹下并發(fā)包下的Executor接口,Executor接口雖然作為一個(gè)非常舊的接口(JDK1.5 2004年發(fā)布),但是很多程序員對(duì)于其中的一些原理還是不熟悉,因此寫這篇文章來介紹下Executor接口,同時(shí)鞏固下自己的知識(shí)。如果文章中有出現(xiàn)錯(cuò)誤,歡迎大家指出。

Executor框架的最頂層實(shí)現(xiàn)是ThreadPoolExecutor類,Executors工廠類中提供的newScheduledThreadPool、newFixedThreadPool、newCachedThreadPool方法其實(shí)也只是ThreadPoolExecutor的構(gòu)造函數(shù)參數(shù)不同而已。通過傳入不同的參數(shù),就可以構(gòu)造出適用于不同應(yīng)用場(chǎng)景下的線程池,那么它的底層原理是怎樣實(shí)現(xiàn)的呢,這篇就來介紹下ThreadPoolExecutor線程池的運(yùn)行過程。

?

corePoolSize: 核心池的大小。 當(dāng)有任務(wù)來之后,就會(huì)創(chuàng)建一個(gè)線程去執(zhí)行任務(wù),當(dāng)線程池中的線程數(shù)目達(dá)到corePoolSize后,就會(huì)把到達(dá)的任務(wù)放到緩存隊(duì)列當(dāng)中
maximumPoolSize: 線程池最大線程數(shù),它表示在線程池中最多能創(chuàng)建多少個(gè)線程;
keepAliveTime: 表示線程沒有任務(wù)執(zhí)行時(shí)最多保持多久時(shí)間會(huì)終止。
unit: 參數(shù)keepAliveTime的時(shí)間單位,有7種取值,在TimeUnit類中有7種靜態(tài)屬性:

newCachedThreadPool

創(chuàng)建一個(gè)可緩存線程池,如果線程池長(zhǎng)度超過處理需要,可靈活回收空閑線程,若無可回收,則新建線程。示例代碼如下:

// 無限大小線程池 jvm自動(dòng)回收 ExecutorService newCachedThreadPool = Executors.newCachedThreadPool(); for (int i = 0; i < 10; i++) {final int temp = i;newCachedThreadPool.execute(new Runnable() {@Overridepublic void run() {try {Thread.sleep(100);} catch (Exception e) {// TODO: handle exception}System.out.println(Thread.currentThread().getName() + ",i:" + temp);}}); }

總結(jié): 線程池為無限大,當(dāng)執(zhí)行第二個(gè)任務(wù)時(shí)第一個(gè)任務(wù)已經(jīng)完成,會(huì)復(fù)用執(zhí)行第一個(gè)任務(wù)的線程,而不用每次新建線程。

newFixedThreadPool

創(chuàng)建一個(gè)定長(zhǎng)線程池,可控制線程最大并發(fā)數(shù),超出的線程會(huì)在隊(duì)列中等待。示例代碼如下:

ExecutorService newFixedThreadPool = Executors.newFixedThreadPool(5);for (int i = 0; i < 10; i++) {final int temp = i;newFixedThreadPool.execute(new Runnable() {@Overridepublic void run() {System.out.println(Thread.currentThread().getId() + ",i:" + temp);}});}

總結(jié):因?yàn)榫€程池大小為3,每個(gè)任務(wù)輸出index后sleep 2秒,所以每?jī)擅氪蛴?個(gè)數(shù)字。

定長(zhǎng)線程池的大小最好根據(jù)系統(tǒng)資源進(jìn)行設(shè)置。如Runtime.getRuntime().availableProcessors()

newScheduledThreadPool

創(chuàng)建一個(gè)定長(zhǎng)線程池,支持定時(shí)及周期性任務(wù)執(zhí)行。延遲執(zhí)行示例代碼如下:

ScheduledExecutorService newScheduledThreadPool = Executors.newScheduledThreadPool(5);for (int i = 0; i < 10; i++) {final int temp = i;newScheduledThreadPool.schedule(new Runnable() {public void run() {System.out.println("i:" + temp);}}, 3, TimeUnit.SECONDS); }

表示延遲3秒執(zhí)行。

newSingleThreadExecutor

創(chuàng)建一個(gè)單線程化的線程池,它只會(huì)用唯一的工作線程來執(zhí)行任務(wù),保證所有任務(wù)按照指定順序(FIFO, LIFO, 優(yōu)先級(jí))執(zhí)行。示例代碼如下:

ExecutorService newSingleThreadExecutor = Executors.newSingleThreadExecutor();for (int i = 0; i < 10; i++) {final int index = i;newSingleThreadExecutor.execute(new Runnable() {@Overridepublic void run() {System.out.println("index:" + index);try {Thread.sleep(200);} catch (Exception e) {// TODO: handle exception}}});}

注意: 結(jié)果依次輸出,相當(dāng)于順序執(zhí)行各個(gè)任務(wù)。

線程池原理剖析

提交一個(gè)任務(wù)到線程池中,線程池的處理流程如下:

1、判斷線程池里的核心線程是否都在執(zhí)行任務(wù),如果不是(核心線程空閑或者還有核心線程沒有被創(chuàng)建)則創(chuàng)建一個(gè)新的工作線程來執(zhí)行任務(wù)。如果核心線程都在執(zhí)行任務(wù),則進(jìn)入下個(gè)流程。

2、線程池判斷工作隊(duì)列是否已滿,如果工作隊(duì)列沒有滿,則將新提交的任務(wù)存儲(chǔ)在這個(gè)工作隊(duì)列里。如果工作隊(duì)列滿了,則進(jìn)入下個(gè)流程。

3、判斷線程池里的線程是否都處于工作狀態(tài),如果沒有,則創(chuàng)建一個(gè)新的工作線程來執(zhí)行任務(wù)。如果已經(jīng)滿了,則交給飽和策略來處理這個(gè)任務(wù)。

自定義線程線程池

如果當(dāng)前線程池中的線程數(shù)目小于corePoolSize,則每來一個(gè)任務(wù),就會(huì)創(chuàng)建一個(gè)線程去執(zhí)行這個(gè)任務(wù);

如果當(dāng)前線程池中的線程數(shù)目>=corePoolSize,則每來一個(gè)任務(wù),會(huì)嘗試將其添加到任務(wù)緩存隊(duì)列當(dāng)中,若添加成功,則該任務(wù)會(huì)等待空閑線程將其取出去執(zhí)行;若添加失敗(一般來說是任務(wù)緩存隊(duì)列已滿),則會(huì)嘗試創(chuàng)建新的線程去執(zhí)行這個(gè)任務(wù);

如果隊(duì)列已經(jīng)滿了,則在總線程數(shù)不大于maximumPoolSize的前提下,則創(chuàng)建新的線程

如果當(dāng)前線程池中的線程數(shù)目達(dá)到maximumPoolSize,則會(huì)采取任務(wù)拒絕策略進(jìn)行處理;

如果線程池中的線程數(shù)量大于 corePoolSize時(shí),如果某線程空閑時(shí)間超過keepAliveTime,線程將被終止,直至線程池中的線程數(shù)目不大于corePoolSize;如果允許為核心池中的線程設(shè)置存活時(shí)間,那么核心池中的線程空閑時(shí)間超過keepAliveTime,線程也會(huì)被終止。

public class Test0007 {public static void main(String[] args) {ThreadPoolExecutor executor = new ThreadPoolExecutor(1, 2, 60L, TimeUnit.SECONDS, new ArrayBlockingQueue<>(3));for (int i = 1; i <= 6; i++) {TaskThred t1 = new TaskThred("任務(wù)" + i);executor.execute(t1);}executor.shutdown();} }class TaskThred implements Runnable {private String taskName;public TaskThred(String taskName) {this.taskName = taskName;}@Overridepublic void run() {System.out.println(Thread.currentThread().getName()+taskName);}}

合理配置線程池

?

CPU密集

CPU密集的意思是該任務(wù)需要大量的運(yùn)算,而沒有阻塞,CPU一直全速運(yùn)行

CPU密集任務(wù)只有在真正的多核CPU上才可能得到加速(通過多線程),而在單核CPU上,無論你開幾個(gè)模擬的多線程,該任務(wù)都不可能得到加速,因?yàn)镃PU總的運(yùn)算能力就那些。

IO密集

IO密集型,即該任務(wù)需要大量的IO,即大量的阻塞。在單線程上運(yùn)行IO密集型的任務(wù)會(huì)導(dǎo)致浪費(fèi)大量的CPU運(yùn)算能力浪費(fèi)在等待。所以在IO密集型任務(wù)中使用多線程可以大大的加速程序運(yùn)行,即時(shí)在單核CPU上,這種加速主要就是利用了被浪費(fèi)掉的阻塞時(shí)間。

?

接著上一篇探討線程池留下的尾巴,如何合理的設(shè)置線程池大小。

要想合理的配置線程池的大小,首先得分析任務(wù)的特性,可以從以下幾個(gè)角度分析:

1.? 任務(wù)的性質(zhì):CPU密集型任務(wù)、IO密集型任務(wù)、混合型任務(wù)。

2.? 任務(wù)的優(yōu)先級(jí):高、中、低。

3.? 任務(wù)的執(zhí)行時(shí)間:長(zhǎng)、中、短。

4.? 任務(wù)的依賴性:是否依賴其他系統(tǒng)資源,如數(shù)據(jù)庫連接等。

性質(zhì)不同的任務(wù)可以交給不同規(guī)模的線程池執(zhí)行。

對(duì)于不同性質(zhì)的任務(wù)來說,CPU密集型任務(wù)應(yīng)配置盡可能小的線程,如配置CPU個(gè)數(shù)+1的線程數(shù),IO密集型任務(wù)應(yīng)配置盡可能多的線程,因?yàn)镮O操作不占用CPU,不要讓CPU閑下來,應(yīng)加大線程數(shù)量,如配置兩倍CPU個(gè)數(shù)+1,而對(duì)于混合型的任務(wù),如果可以拆分,拆分成IO密集型和CPU密集型分別處理,前提是兩者運(yùn)行的時(shí)間是差不多的,如果處理時(shí)間相差很大,則沒必要拆分了。

若任務(wù)對(duì)其他系統(tǒng)資源有依賴,如某個(gè)任務(wù)依賴數(shù)據(jù)庫的連接返回的結(jié)果,這時(shí)候等待的時(shí)間越長(zhǎng),則CPU空閑的時(shí)間越長(zhǎng),那么線程數(shù)量應(yīng)設(shè)置得越大,才能更好的利用CPU。

當(dāng)然具體合理線程池值大小,需要結(jié)合系統(tǒng)實(shí)際情況,在大量的嘗試下比較才能得出,以上只是前人總結(jié)的規(guī)律。

?

最佳線程數(shù)目 = ((線程等待時(shí)間+線程CPU時(shí)間)/線程CPU時(shí)間 )* CPU數(shù)目

比如平均每個(gè)線程CPU運(yùn)行時(shí)間為0.5s,而線程等待時(shí)間(非CPU運(yùn)行時(shí)間,比如IO)為1.5s,CPU核心數(shù)為8,那么根據(jù)上面這個(gè)公式估算得到:((0.5+1.5)/0.5)*8=32。這個(gè)公式進(jìn)一步轉(zhuǎn)化為:

最佳線程數(shù)目 = (線程等待時(shí)間與線程CPU時(shí)間之比 + 1)* CPU數(shù)目

可以得出一個(gè)結(jié)論:?
線程等待時(shí)間所占比例越高,需要越多線程。線程CPU時(shí)間所占比例越高,需要越少線程。?
以上公式與之前的CPU和IO密集型任務(wù)設(shè)置線程數(shù)基本吻合。

?

CPU密集型時(shí),任務(wù)可以少配置線程數(shù),大概和機(jī)器的cpu核數(shù)相當(dāng),這樣可以使得每個(gè)線程都在執(zhí)行任務(wù)

IO密集型時(shí),大部分線程都阻塞,故需要多配置線程數(shù),2*cpu核數(shù)

操作系統(tǒng)之名稱解釋:

某些進(jìn)程花費(fèi)了絕大多數(shù)時(shí)間在計(jì)算上,而其他則在等待I/O上花費(fèi)了大多是時(shí)間,

前者稱為計(jì)算密集型(CPU密集型)computer-bound,后者稱為I/O密集型,I/O-bound。

總結(jié)

以上是生活随笔為你收集整理的threadpoolexecutor底层实现原理的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。