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