—讀《java并發(fā)編程》筆記
為什么需要線程執(zhí)行框架
不用線程池是否也有異步執(zhí)行任務(wù)的方案?
有兩種方案,一是單獨(dú)起一個(gè)線程,任務(wù)由這個(gè)線程順序執(zhí)行。二是每個(gè)任務(wù)創(chuàng)建一個(gè)新線程,每個(gè)任務(wù)都由一個(gè)新線程去執(zhí)行。
然而兩種方案各有缺點(diǎn):
第一種方案,因?yàn)槭琼樞驁?zhí)行,在任務(wù)的響應(yīng)效率有較大影響。
第二種方案,創(chuàng)建線程是有資源消耗的,對(duì)于web應(yīng)用這樣的大流量請(qǐng)求,很容易就會(huì)達(dá)到資源瓶頸。
另外需要考慮的一些問題:
- 可否知道當(dāng)前任務(wù)在被哪個(gè)線程運(yùn)行了。
- 任務(wù)的執(zhí)行順序是怎樣的,先入先出?優(yōu)先隊(duì)列?
- 多少任務(wù)正在同時(shí)執(zhí)行
- 如果當(dāng)前已經(jīng)達(dá)到了系統(tǒng)性能瓶頸,怎么決定任務(wù)的優(yōu)先級(jí),哪些任務(wù)需要被移出執(zhí)行隊(duì)列。
- 任務(wù)執(zhí)行前的準(zhǔn)備,以及執(zhí)行后的清理工作(釋放資源)如何做。
為解決上面這些問題,客觀上需要有一個(gè)管理線程資源的框架。這個(gè)框架可以優(yōu)化線程執(zhí)行所占資源,定制靈活的執(zhí)行策略,讓異步任務(wù)的執(zhí)行更為健壯。
線程池及其創(chuàng)建
在java體系中,管理線程資源的框架便是線程池(thread pool)。線程池,顧名思義就是線程的資源池,是一種容器資源。它的主要作用就是讓線程可以重用,并且保持一定數(shù)量的活躍線程,從而使任務(wù)響應(yīng)時(shí)間縮短,通過對(duì)線程池運(yùn)行參數(shù)的調(diào)優(yōu),可以達(dá)到任務(wù)吞吐量和相應(yīng)時(shí)間的最優(yōu)。
線程池的創(chuàng)建,一般情況下,是通過concurrent包提供的工廠方法Executors
比如
Executors.newFixedThreadPool(int nThreads) 會(huì)返回一個(gè)固定線程數(shù)量的實(shí)現(xiàn)了ExecutorService接口的線程池服務(wù),如果當(dāng)前任務(wù)的提交數(shù)量超過了這個(gè)限定數(shù)量,會(huì)將其排入一個(gè)LinkedBlockingQueue中排隊(duì)。
除此之外,還有
Executors.newSingleThreadExecutor, Executors.newCachedThreadPool 等等
當(dāng)然,如果看到過Executors 的源代碼, 你就知道,還可以自己創(chuàng)建一個(gè)線程池,比如上述Executors.newFixedThreadPool(int nThreads) 其源碼就是
return new ThreadPoolExecutor(nThreads, nThreads,0L, TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>(),threadFactory);
線程池的生命周期
線程池的生命周期有三個(gè)階段,首先是running 然后是 shut down 最終是terminated
- running 顧名思義就是正常運(yùn)行狀態(tài)
- shut down 執(zhí)行shutdownNow()后達(dá)到的狀態(tài),這個(gè)狀態(tài)下線程池不再接受新任務(wù),等待任務(wù)也阻*止。但是此時(shí)任務(wù)不一定運(yùn)行完,事實(shí)上這個(gè)方法會(huì)返回一個(gè)list包括了還在運(yùn)行的任務(wù)列表。
- terminated 就是在shutdown后所有任務(wù)已經(jīng)完成的狀態(tài),此時(shí)沒有任務(wù)在運(yùn)行了。
線程池的參數(shù)
- corePoolSize 核心線程數(shù)。這個(gè)參數(shù)的含義是線程池需要保持的最低線程數(shù),不管當(dāng)前有沒有任務(wù)在運(yùn)行,線程池中都會(huì)保持這個(gè)配置數(shù)量的活躍線程。當(dāng)任務(wù)數(shù)量超過corePoolSize時(shí),會(huì)被丟到工作隊(duì)列中,直到隊(duì)列滿之前,線程池中的線程數(shù)不會(huì)增加。
- maxPoolSize :最大線程數(shù)。該參數(shù)指明了一個(gè)線程池中同時(shí)可以有多少活躍線程同時(shí)運(yùn)行。
- keepAliveTime 線程最大存活時(shí)間。 當(dāng)池子中的線程數(shù)量超過了corePoolSize時(shí),這個(gè)數(shù)值代表了那些超出的空閑線程可以存活的最大時(shí)間。
- workQueue:自定義的線程池隊(duì)列實(shí)例。有三種隊(duì)列類型可以選擇,分別是無界隊(duì)列,有界隊(duì)列,以及 同步隊(duì)列(SynchronousQueue),其中Executors.newFixedThreadPool 使用的LinkedBlockingQueue 屬于無界隊(duì)列, Executors.newCachedThreadPool 使用的是同步隊(duì)列。其中,使用默認(rèn)的無界隊(duì)列,可能會(huì)存在在并發(fā)量比較高時(shí),隊(duì)列占用內(nèi)存過高可能導(dǎo)致其溢出的風(fēng)險(xiǎn)。
queue的配置可以同前面幾個(gè)參數(shù)配合,以達(dá)到不同的效果:
比如:- 按照優(yōu)先級(jí)處理一組數(shù)據(jù),可以傳入PriorityQueue 根據(jù)指定參數(shù)配置優(yōu)先級(jí), 這個(gè)時(shí)候隊(duì)列長隊(duì)可以設(shè)置相對(duì)較長,而corePoolSize可以設(shè)置相對(duì)小,從而達(dá)到吞吐量與占用資源的最優(yōu)配置。
- 對(duì)于大量短時(shí)間訪問的請(qǐng)求,考慮用SynchronousQueue。同時(shí)設(shè)置一個(gè)相對(duì)較短的keepAliveTime以及極大的maxPoolSize, SynchronousQueue的特性是如果要把一個(gè)元素加入隊(duì)列,需要有另外一個(gè)線程等待接收,如果另外一個(gè)線程沒有,則創(chuàng)建一個(gè)。這個(gè)特性很適合處理即時(shí)的消息傳遞,可以支撐較大的容量。
- RejectedExecutionHandler: 隊(duì)列飽和處理器。 當(dāng)工作隊(duì)列已經(jīng)滿時(shí),要決定哪些任務(wù)保留哪些任務(wù)要丟棄,有幾種策略可以選擇,比如:AbortPolicy, CallerRunsPolicy,DiscardPolicy。 可以根據(jù)實(shí)際情況靈活選擇。