摘要:雖然使用很方便,但是建議大家使用已經(jīng)設(shè)定的幾種線程池?zé)o界線程池,可以進(jìn)行線程自動(dòng)回收固定大小線程池和單個(gè)后線程,它們滿足大部分的場(chǎng)景需求。固定大小線程池和有些類似,只不過從單線程變成可以指定線程數(shù)量,依舊為無限。
池的概念在java中也是常見,還有連接池、常量池等,池的作用也是類似的,對(duì)于對(duì)象、資源的重復(fù)利用,減小系統(tǒng)開銷,提升運(yùn)行效率。
線程池的主要功能:
1.減少創(chuàng)建和銷毀線程的次數(shù),提升運(yùn)行性能,尤其是在大量異步任務(wù)時(shí)
2.可以更合理地管理線程,如:線程的運(yùn)行數(shù)量,防止同一時(shí)間大量任務(wù)運(yùn)行,導(dǎo)致系統(tǒng)崩潰
先舉個(gè)demo,看看使用線程池的區(qū)別,線程池:
AtomicLong al = new AtomicLong(0l); Long s1 = System.currentTimeMillis(); ThreadPoolExecutor pool = new ThreadPoolExecutor(5, 100000, 100, TimeUnit.SECONDS, new LinkedBlockingDeque(20000)); for(int i=0;i<20000;i++){ pool.execute(new Runnable() { @Override public void run() { try { al.incrementAndGet(); } catch (Exception e) { e.printStackTrace(); } } }); } pool.shutdown(); pool.awaitTermination(1, TimeUnit.HOURS); System.out.println("耗時(shí):"+(System.currentTimeMillis()-s1)); System.out.println("AtomicLong="+al.get());
結(jié)果:
耗時(shí):224 AtomicLong=20000
非線程池:
AtomicLong al = new AtomicLong(0l); Long s1 = System.currentTimeMillis(); for(int i=0;i<20000;i++){ Thread t = new Thread(new Runnable() { @Override public void run() { try { al.incrementAndGet(); } catch (Exception e) { e.printStackTrace(); } } }); t.start(); } while(true){ if(al.get()==20000){ System.out.println("耗時(shí):"+(System.currentTimeMillis()-s1)); System.out.println("AtomicLong="+al.get()); break; } Thread.sleep(1); }
結(jié)果:
耗時(shí):2972 AtomicLong=20000
從耗時(shí)看2者相差了13倍,差距還是挺大的,可見有大量異步任務(wù)時(shí)使用線程池能夠提升性能。
線程池的主要參數(shù)public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueueworkQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler) {
線程池的主要參數(shù)有這6個(gè):
corePoolSize:核心池的大小,核心池的線程不會(huì)被回收,沒有任務(wù)就處于空閑狀態(tài)。
maximumPoolSize:線程池最大允許的線程數(shù),
keepAliveTime:當(dāng)線程數(shù)超過corePoolSize時(shí),但小于等于maximumPoolSize,會(huì)生成‘臨時(shí)’線程,‘臨時(shí)’線程執(zhí)行完任務(wù)不會(huì)馬上被回收,如果在keepAliveTime時(shí)間內(nèi),還沒有新任務(wù)的話,才會(huì)被回收。
unit:keepAliveTime的單位。
workQueue:當(dāng)前線程數(shù)超過corePoolSize大小時(shí),新任務(wù)會(huì)處在等待狀態(tài),存在workQueue中。
threadFactory:生成新線程的工廠。
handler:當(dāng)線程數(shù)超過workQueue的上限時(shí),新線程會(huì)被拒絕,這個(gè)參數(shù)就是新線程被拒絕的方式。
其中corePoolSize和maximumPoolSize相對(duì)難理解,再詳細(xì)說明:
1、池中線程數(shù)小于corePoolSize,新任務(wù)都不排隊(duì)而是直接添加新線程
2、池中線程數(shù)大于等于corePoolSize,workQueue未滿,首選將新任務(wù)加入workQueue而不是添加新線程
3、池中線程數(shù)大于等于corePoolSize,workQueue已滿,但是線程數(shù)小于maximumPoolSize,添加新的線程來處理被添加的任務(wù)
4、池中線程數(shù)大于大于corePoolSize,workQueue已滿,并且線程數(shù)大于等于maximumPoolSize,新任務(wù)被拒絕,使用handler處理被拒絕的任務(wù)
因此maximumPoolSize、corePoolSize不宜設(shè)置過大,否則會(huì)造成內(nèi)存、cpu過載的問題,workQueue而盡量可以大一些。
Executors雖然ThreadPoolExecutor使用很方便,但是建議大家使用jdk已經(jīng)設(shè)定的幾種線程池:
Executors.newCachedThreadPool()(無界線程池,可以進(jìn)行線程自動(dòng)回收)、Executors.newFixedThreadPool(int)(固定大小線程池)和Executors.newSingleThreadExecutor()(單個(gè)后線程),它們滿足大部分的場(chǎng)景需求。
1.newSingleThreadExecutor 單線程線程池
看下它的實(shí)現(xiàn)方式:
public static ExecutorService newSingleThreadExecutor() { return new FinalizableDelegatedExecutorService (new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue())); }
把corePoolSize設(shè)為1,而workQueue大小設(shè)為無限大,因此永遠(yuǎn)只有一個(gè)線程在執(zhí)行任務(wù),新任務(wù)都在workQueue中等待。
2.newFixedThreadPool 固定大小線程池
public static ExecutorService newFixedThreadPool(int nThreads) { return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue()); }
和newSingleThreadExecutor 有些類似,只不過從單線程變成可以指定線程數(shù)量,workQueue依舊為無限。
3.newCachedThreadPool
public static ExecutorService newCachedThreadPool() { return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue()); }
newCachedThreadPool所有新任務(wù)都會(huì)被立即執(zhí)行,corePoolSize 設(shè)置為0,maximumPoolSize設(shè)置為整數(shù)最大值,所有線程超過60秒未被使用,就會(huì)被銷毀。
workQueue當(dāng)前線程數(shù)超過corePoolSize大小時(shí),新任務(wù)會(huì)處在等待狀態(tài),存在workQueue中。workQueue的實(shí)現(xiàn)方式也就是任務(wù)的等待策略,分為3種:有限隊(duì)列、無限隊(duì)列、直接提交。
談?wù)勄?種,jdk使用了LinkedBlockingQueue而非ArrayBlockingQueue,有限隊(duì)列的優(yōu)勢(shì)在于對(duì)資源和效率更定制化的配置,但是對(duì)比無限隊(duì)列缺點(diǎn)也很明顯:
1).增加開發(fā)難度。需要考慮到corePoolSize ,maximumPoolSize,workQueue,3個(gè)參數(shù)大小,既要使任務(wù)高效地執(zhí)行,又要避免任務(wù)超量的問題,對(duì)于開發(fā)和測(cè)試的復(fù)雜度提高很多。
2).防止業(yè)務(wù)突刺。在互聯(lián)網(wǎng)應(yīng)用業(yè)務(wù)量暴增也是必須考慮的問題,在使用無限隊(duì)列時(shí),雖然執(zhí)行可能慢一點(diǎn),但是能保證執(zhí)行到。使用有限隊(duì)列,會(huì)出現(xiàn)任務(wù)拒絕的問題。
綜上考慮,更建議使用無限隊(duì)列,尤其是對(duì)于多線程不是很熟悉的朋友們。
所謂拒絕策略之前也提到過了,任務(wù)太多,超過maximumPoolSize了怎么辦?當(dāng)然是接不下了,接不下那只有拒絕了。拒絕的時(shí)候可以指定拒絕策略,也就是一段處理程序。
決絕策略的父接口是RejectedExecutionHandler,JDK本身在ThreadPoolExecutor里給用戶提供了四種拒絕策略,看一下:
1、AbortPolicy
直接拋出一個(gè)RejectedExecutionException,這也是JDK默認(rèn)的拒絕策略
2、CallerRunsPolicy
嘗試直接運(yùn)行被拒絕的任務(wù),如果線程池已經(jīng)被關(guān)閉了,任務(wù)就被丟棄。
3、DiscardOldestPolicy
移除最晚的那個(gè)沒有被處理的任務(wù),然后執(zhí)行被拒絕的任務(wù)。同樣,如果線程池已經(jīng)被關(guān)閉了,任務(wù)就被丟棄了
4、DiscardPolicy
悄悄地拒絕任務(wù)
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/76376.html
摘要:高并發(fā)系列第篇文章。簡單的說,在使用了線程池之后,創(chuàng)建線程變成了從線程池中獲取一個(gè)空閑的線程,然后使用,關(guān)閉線程變成了將線程歸還到線程池。如果調(diào)用了線程池的方法,線程池會(huì)提前把核心線程都創(chuàng)造好,并啟動(dòng)線程池允許創(chuàng)建的最大線程數(shù)。 java高并發(fā)系列第18篇文章。 本文主要內(nèi)容 什么是線程池 線程池實(shí)現(xiàn)原理 線程池中常見的各種隊(duì)列 自定義線程創(chuàng)建的工廠 常見的飽和策略 自定義飽和策略 ...
摘要:下面是線程相關(guān)的熱門面試題,你可以用它來好好準(zhǔn)備面試。線程安全問題都是由全局變量及靜態(tài)變量引起的。持有自旋鎖的線程在之前應(yīng)該釋放自旋鎖以便其它線程可以獲得自旋鎖。 最近看到網(wǎng)上流傳著,各種面試經(jīng)驗(yàn)及面試題,往往都是一大堆技術(shù)題目貼上去,而沒有答案。 不管你是新程序員還是老手,你一定在面試中遇到過有關(guān)線程的問題。Java語言一個(gè)重要的特點(diǎn)就是內(nèi)置了對(duì)并發(fā)的支持,讓Java大受企業(yè)和程序員...
摘要:本人郵箱歡迎轉(zhuǎn)載轉(zhuǎn)載請(qǐng)注明網(wǎng)址代碼已經(jīng)全部托管有需要的同學(xué)自行下載引言在之前的例子我們要?jiǎng)?chuàng)建多個(gè)線程處理一批任務(wù)的時(shí)候我是通過創(chuàng)建線程數(shù)組或者使用線程集合來管理的但是這樣做不太好因?yàn)檫@些線程沒有被重復(fù)利用所以這里要引入線程池今天我們就講線程 本人郵箱: 歡迎轉(zhuǎn)載,轉(zhuǎn)載請(qǐng)注明網(wǎng)址 http://blog.csdn.net/tianshi_kcogithub: https://github...
摘要:進(jìn)程線程與協(xié)程它們都是并行機(jī)制的解決方案。選擇是任意性的,并在對(duì)實(shí)現(xiàn)做出決定時(shí)發(fā)生。線程池的大小一旦達(dá)到最大值就會(huì)保持不變,如果某個(gè)線程因?yàn)閳?zhí)行異常而結(jié)束,那么線程池會(huì)補(bǔ)充一個(gè)新線程。此線程池支持定時(shí)以及周期性執(zhí)行任務(wù)的需求。 并發(fā)與并行的概念 并發(fā)(Concurrency): 問題域中的概念—— 程序需要被設(shè)計(jì)成能夠處理多個(gè)同時(shí)(或者幾乎同時(shí))發(fā)生的事件 并行(Parallel...
摘要:多線程和并發(fā)問題是技術(shù)面試中面試官比較喜歡問的問題之一。線程可以被稱為輕量級(jí)進(jìn)程。一個(gè)守護(hù)線程是在后臺(tái)執(zhí)行并且不會(huì)阻止終止的線程。其他的線程狀態(tài)還有,和。上下文切換是多任務(wù)操作系統(tǒng)和多線程環(huán)境的基本特征。 多線程和并發(fā)問題是 Java 技術(shù)面試中面試官比較喜歡問的問題之一。在這里,從面試的角度列出了大部分重要的問題,但是你仍然應(yīng)該牢固的掌握J(rèn)ava多線程基礎(chǔ)知識(shí)來對(duì)應(yīng)日后碰到的問題。(...
閱讀 3479·2021-11-25 09:43
閱讀 2628·2021-09-22 15:54
閱讀 607·2019-08-30 15:55
閱讀 984·2019-08-30 15:55
閱讀 2010·2019-08-30 15:55
閱讀 1754·2019-08-30 15:53
閱讀 3479·2019-08-30 15:52
閱讀 2049·2019-08-30 12:55