Java中的線程池是運用場景最多的併發框架,幾乎全部須要異步或併發執行任務的程序均可以使用線程池。在開發過程當中,合理地使用線程池可以帶來3個好處。java
線程池是爲忽然大量爆發的線程設計的,經過有限的幾個固定線程爲大量的操做服務,減小了建立和銷燬線程所需的時間,從而提升效率。
若是一個線程的時間很是長,就不必用線程池了(不是不能做長時間操做,而是不宜。),何況咱們還不能控制線程池中線程的開始、掛起、和停止數據庫
Executor框架的最頂層實現是ThreadPoolExecutor類,Executors工廠類中提供的newScheduledThreadPool、newFixedThreadPool、newCachedThreadPool方法其實也只是ThreadPoolExecutor的構造函數參數不一樣而已。經過傳入不一樣的參數,就能夠構造出適用於不一樣應用場景下的線程池
corePoolSize: 核心池的大小。 當有任務來以後,就會建立一個線程去執行任務,當線程池中的線程數目達到corePoolSize後,就會把到達的任務放到緩存隊列當中
maximumPoolSize: 線程池最大線程數,它表示在線程池中最多能建立多少個線程;
keepAliveTime: 表示線程沒有任務執行時最多保持多久時間會終止。
unit: 參數keepAliveTime的時間單位,有7種取值緩存
建立一個可緩存線程池,若是線程池長度超過處理須要,可靈活回收空閒線程,若無可回收,則新建線程。
線程池爲無限大,當執行第二個任務時第一個任務已經完成,會複用執行第一個任務的線程,而不用每次新建線程多線程
建立一個定長線程池,可控制線程最大併發數,超出的線程會在隊列中等待
定長線程池的大小最好根據系統資源進行設置。如Runtime.getRuntime().availableProcessors()併發
建立一個定長線程池,支持定時及週期性任務執行app
建立一個單線程化的線程池,它只會用惟一的工做線程來執行任務,保證全部任務按照指定順序(FIFO, LIFO, 優先級)執行框架
提交一個任務到線程池中,線程池的處理流程以下:
一、判斷線程池裏的核心線程是否都在執行任務,若是不是(核心線程空閒或者還有核心線程沒有被建立)則建立一個新的工做線程來執行任務。若是核心線程都在執行任務,則進入下個流程。
二、線程池判斷工做隊列是否已滿,若是工做隊列沒有滿,則將新提交的任務存儲在這個工做隊列裏。若是工做隊列滿了,則進入下個流程。
三、判斷線程池裏的線程是否都處於工做狀態,若是沒有,則建立一個新的工做線程來執行任務。若是已經滿了,則交給飽和策略來處理這個任務。異步
CPU密集的意思是該任務須要大量的運算,而沒有阻塞,CPU一直全速運行。
CPU密集任務只有在真正的多核CPU上纔可能獲得加速(經過多線程),而在單核CPU上,不管你開幾個模擬的多線程,該任務都不可能獲得加速,由於CPU總的運算能力就那些。ide
IO密集型,即該任務須要大量的IO,即大量的阻塞。在單線程上運行IO密集型的任務會致使浪費大量的CPU運算能力浪費在等待。因此在IO密集型任務中使用多線程能夠大大的加速程序運行,即時在單核CPU上,這種加速主要就是利用了被浪費掉的阻塞時間函數
要想合理的配置線程池的大小,首先得分析任務的特性,能夠從如下幾個角度分析:
性質不一樣的任務能夠交給不一樣規模的線程池執行。
對於不一樣性質的任務來講,CPU密集型任務應配置儘量小的線程,如配置CPU個數+1的線程數,IO密集型任務應配置儘量多的線程,由於IO操做不佔用CPU,不要讓CPU閒下來,應加大線程數量,如配置兩倍CPU個數+1,而對於混合型的任務,若是能夠拆分,拆分紅IO密集型和CPU密集型分別處理,前提是二者運行的時間是差很少的,若是處理時間相差很大,則不必拆分了。
若任務對其餘系統資源有依賴,如某個任務依賴數據庫的鏈接返回的結果,這時候等待的時間越長,則CPU空閒的時間越長,那麼線程數量應設置得越大,才能更好的利用CPU。
固然具體合理線程池值大小,須要結合系統實際狀況,在大量的嘗試下比較才能得出,以上只是前人總結的規律。
最佳線程數目 = ((線程等待時間+線程CPU時間)/線程CPU時間 )* CPU數目
好比平均每一個線程CPU運行時間爲0.5s,而線程等待時間(非CPU運行時間,好比IO)爲1.5s,CPU核心數爲8,那麼根據上面這個公式估算獲得:((0.5+1.5)/0.5)*8=32。這個公式進一步轉化爲:
最佳線程數目 = (線程等待時間與線程CPU時間之比 + 1)* CPU數目
能夠得出一個結論:
線程等待時間所佔比例越高,須要越多線程。線程CPU時間所佔比例越高,須要越少線程。
以上公式與以前的CPU和IO密集型任務設置線程數基本吻合。
CPU密集型時,任務能夠少配置線程數,大概和機器的cpu核數至關,這樣可使得每一個線程都在執行任務
IO密集型時,大部分線程都阻塞,故須要多配置線程數,2*cpu核數
操做系統之名稱解釋:
某些進程花費了絕大多數時間在計算上,而其餘則在等待I/O上花費了大可能是時間,
前者稱爲計算密集型(CPU密集型)computer-bound,後者稱爲I/O密集型,I/O-bound。
@RequestMapping("test-threadpool") public void testThreadPool() { ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(1, 6, 60L, TimeUnit.SECONDS, new ArrayBlockingQueue<>(3)); for (int i = 0; i < 6; i++) { TaskThred t1 = new TaskThred("任務" + i); threadPoolExecutor.execute(t1); } threadPoolExecutor.shutdown(); }
class TaskThred implements Runnable { private String taskName; public TaskThred(String taskName) { this.taskName = taskName; } @Override public void run() { System.out.println(Thread.currentThread().getName()+taskName); } }
pool-1-thread-2任務4pool-1-thread-1任務0pool-1-thread-1任務1pool-1-thread-1任務2pool-1-thread-1任務3pool-1-thread-3任務5