java線程池,利用Exceutors建立不一樣的線程池知足不一樣場景需求:java
- newSingleThreadExecutor() 建立一個單線程的線程池。這個線程池只有一個線程在工做,也就是至關於單線程串行執行全部任務。若是這個惟一的線程由於異常結束,那麼會有一個新的線程來替代它。此線程池保證全部任務的執行順序按照任務的提交順序執行。
- newFixedThreadPool(int nThreads) 建立固定大小的線程池。每次提交一個任務就建立一個線程,直到線程達到線程池的最大大小。線程池的大小一旦達到最大值就會保持不變,若是某個線程由於執行異常而結束,那麼線程池會補充一個新線程。
- newCachedThreadPool() 建立一個可緩存的線程池。若是線程池的大小超過了處理任務所須要的線程, 那麼就會回收部分空閒(默認60秒不執行任務)的線程,當任務數增長時,此線程池又能夠智能的添加新線程來處理任務。此線程池不會對線程池大小作限制,線程池大小徹底依賴於操做系統(或者說JVM)可以建立的最大線程大小
- 優勢就是靈活建立線程,因地制宜,任務少時很省資源。缺點就是可建立的線程上限太大,源代碼裏是Integer.MAX_VALUE大小,這個數量有點可怕
- newScheduledThreadPool() 建立一個大小無限的線程池。此線程池支持定時以及週期性執行任務的需求。
- newWorkStealingPool() jdk8引入的,內部會構建ForkJoinPool,利用working-stealing算法,並行的處理任務,可是不保證處理順序

Fork/Join框架算法
- 把大任務分割成若干個小任務並行執行,最終彙總每一個小任務結果後獲得大任務的框架
- work-stealing算法:某個線程從其餘線程隊列裏竊取任務來執行

由於分割成若干個小任務由多個線程去執行,就會出現有的線程已經完成任務而有的還未完成任務,已經完成的線程就閒置了,爲了提高效率,讓已經完成任務的線程去其餘線程竊取隊列裏的任務來執行。爲了減小竊取線程對其餘線程的競爭,一般會使用雙端隊列,執行任務的線程從頭部拿任務執行,竊取線程是從隊列尾部拿任務執行編程
問:爲何要使用線程池?緩存
- 下降資源消耗(經過重複利用已建立的線程來工做,下降建立線程和銷燬線程的消耗)
- 提升線程的可管理性(線程是稀缺資源,若是無限制的建立會不只會消耗系統資源還會下降系統的穩定性,使用線程池能夠統一的分配、調優、監控)
Executor的框架圖多線程

JUC的三個Executor接口併發
- Executor:運行新任務的簡單接口,將人物提交和任務執行細節解耦
- 經過源碼看到newCachedThreadPool是返回的ExecutorService newSingleTreadExecutor是返回的FinalizableDelegatedExecutorService 最終都是繼承的Executor








- 接口Executor只有一個方法就是execute,對於不一樣的實現它多是建立一個新線程當即啓動,也多是使用已有的線程來運行傳入的任務,也多是根據線程池容量或阻塞隊列的容量來決定是否將傳入的任務放入阻塞隊列中或者拒絕接受任務
- ExecutorService:具有管理執行器和任務生命週期方法,提交任務機制更完善
- ExecutorService是Executor的擴展接口 提供了更方便的管理方法 最經常使用的是 shutdown submit
- submit參數有Callable、Runnable兩種 並返回Future

- ScheduledExecutorService:支持Future和按期執行任務
ThreadPoolExecutor的構造函數框架
- corePoolSize:核心線程數量
- maximunPoolSize:線程不夠用時可以建立的最大線程數
- workQueue:任務等待隊列(當前線程數量大於等於corePoolSize的時候,將任務封裝成work放入workQueue中。不一樣的隊列排隊機制不一樣)
- keepAliveTime:線程池維護線程的空閒時間,線程空閒超過這個時間就會被銷燬
- threadFactory:建立新線程,默認使用Executors.defaultThreadFactory(),新建立的線程是同樣的優先級、非守護線程
ps:newCachedThreadPool傳入的隊列是容量爲0的SynchronousQueue,(Java 6的併發編程包中的SynchronousQueue是一個沒有數據緩衝的BlockingQueue,生產者線程對其的插入操做put必須等待消費者的移除操做take,反過來也同樣)函數
handler:線程池的飽和策略ui
- AbortPolicy:直接拋出異常,這是默認策略
- CallerRunsPolicy: 用調用者所在多線程來執行任務
- DiscardOldestPolicy:丟棄隊列中最靠前的任務,並執行當前任務
- DiscardPolicy:直接丟棄任務
- 實現RejectedExecutionHandler接口自定義handler處理
execute方法執行流程以下:編碼

線程池的狀態:
- RUNNING:可以接受新任務,而且也能處理阻塞隊列中的任務
- SHUTDOWN:不能接受新任務,但能夠處理存量任務
- STOP:再也不接受新任務,也不處理存量任務
- TIDYING:全部任務都已終止,正在進行最後的打掃工做,有效線程數爲0
- TERMINATED:terminated()方法執行完成後進入該狀態(該方法什麼也不作只是標識)
狀態轉換圖:

工做線程的生命週期:

問:如何選擇線程池大小?(沒有絕對的算法或規定,是靠經驗累計總結出來的)
- CPU密集型:線程數=按照核數或者核數+1(由於若是線程太多會致使過多的上下文切換,致使沒必要要的開銷)
- I/O密集型:線程數量=CPU核數*(1+平均等待時間/平均工做時間)
ps:
阿里編碼規範指出:線程池不容許使用Executors去建立,而是經過ThreadPoolExecutor的方式,這樣的處理方式讓寫的同窗更加明確線程池的運行規則,規避資源耗盡的風險。 說明:Executors各個方法的弊端:
1)newFixedThreadPool和newSingleThreadExecutor:
主要問題是堆積的請求處理隊列可能會耗費很是大的內存,甚至OOM。
2)newCachedThreadPool和newScheduledThreadPool:
主要問題是線程數最大數是Integer.MAX_VALUE,可能會建立數量很是多的線程,甚至OOM。
例子:使用Guava的ThreadFactoryBuilder


輸出:


不加劇試的輸出是:

從例子中看出 maxPoolSize + QueueSize < taskNum 就會拋出拒絕異常 若是不catch這個異常程序沒法結束(這裏重試機制只是個demo,正確的作法是實現RejectedExecutionHandler接口自定義handler處理)