咱們都知道,每一次建立一個線程,JVM後面的工做包括:爲線程創建虛擬機棧、本地方法棧、程序計數器的內存空間(下圖可看出),因此線程過多容易致使內存空間溢出。同時,當頻繁的建立和銷燬線程容易浪費系統的計算能力在資源的回收和申請中。緩存
另外:建立過多的線程,會致使cpu在線程中的切換時間比處理時間還多,大大下降了系統的吞吐量。所以咱們使用線程池以下好處:安全
再給你們看看阿里開發規約裏面是怎麼說的併發
我知道大多數人都但願先看看線程池怎麼建立,而後再深刻了解。下面給你們一個demoide
1 //存聽任務的阻塞隊列 2 BlockingDeque<Runnable> queue = new LinkedBlockingDeque<>(10); 3 //BasicThreadFactory是本身實現ThreadFactory接口而來 4 BasicThreadFactory factory = new BasicThreadFactory(); 5 ThreadPoolExecutor poolExecutor = new ThreadPoolExecutor(3, 10, 60, 6 TimeUnit.SECONDS, queue, factory, 7 (Runnable r, ThreadPoolExecutor executor)->{ 8 System.out.println(executor.getQueue().size()+"消息隊列已滿"); 9 System.out.println("拒絕服務"); 10 11 });
通常咱們推薦使用ThreadPoolExecutor()自定義建立線程池,由於這比較靈活切可控。函數
int corePoolSize 核心線程數,即肯定有多少個核心線程。
int maximumPoolSize 最大線程數,即限定線程池中的最大線程數量。
long keepAliveTime 非核心線程的存活時間,配合下面的TimeUnit參數肯定時間。
TimeUnit unit 一個時間類型的枚舉類。有從納秒到天的時間量度,配合上面的keepAliveTime肯定非核心線程的存活時間。
BlockingQueue<Runnable> workQueue 裝載Runnable的阻塞隊列,具體類型能夠本身肯定。
ThreadFactory threadFactory 線程工廠,這是一個函數式接口,裏面只定義了一個newThread(Runnable task)方法,須要本身實現工廠的方法,在這裏咱們能夠對線程進行自定義的初始化,例如給線程設定名字,這樣方便後期的調試。
RejectedExecutionHandler handler 拒絕服務處理,這也是一個函數式接口,咱們須要實現rejectedExecution(Runnable r, ThreadPoolExecutor executor)這個方法,這裏能夠根據需求自定義你但願在處理邏輯。固然Java裏面也有已經定義好的四種策略靜態類。能夠經過ThreadPoolExecutor調用
下面介紹的線程池類型,是Jdk幫咱們制定好的策略。可是,有的線程池類型中,要麼存在線程數量無限制、要麼存在阻塞隊列長度無限制,可是這些應該在開發中避免,由於一旦併發太高,會致使大量的對象積壓,致使JVM內存溢出。spa
寫在前面:jdk提供了默認的工廠方法和默認的默認的拒絕處理策略。線程
默認拒絕策略是:不執行並拋出異常設計
默認的工廠方法是:對線程進行安全檢查並命名。調試
1 static class DefaultThreadFactory implements ThreadFactory { 2 private static final AtomicInteger poolNumber = new AtomicInteger(1); 3 private final ThreadGroup group; 4 private final AtomicInteger threadNumber = new AtomicInteger(1); 5 private final String namePrefix; 6 7 DefaultThreadFactory() { 8 SecurityManager s = System.getSecurityManager(); 9 group = (s != null) ? s.getThreadGroup() : 10 Thread.currentThread().getThreadGroup(); 11 namePrefix = "pool-" + 12 poolNumber.getAndIncrement() + 13 "-thread-"; 14 } 15 16 public Thread newThread(Runnable r) { 17 Thread t = new Thread(group, r, 18 namePrefix + threadNumber.getAndIncrement(), 19 0); 20 if (t.isDaemon()) 21 t.setDaemon(false); 22 if (t.getPriority() != Thread.NORM_PRIORITY) 23 t.setPriority(Thread.NORM_PRIORITY); 24 return t; 25 } 26 }
特色:它的核心線程數量就是最大線程數,因此線程池內的線程永遠不會消亡,它採用了無參數的鏈表阻塞隊列,最大的任務數可達232-1個。所以存在任務積壓致使內存溢出的風險。code
2. CachedThreadPool 緩存線程池
特色:沒有核心線程,線程池不能知足任務運行時會建立新的線程,線程數量沒有上限。默認的消亡時間爲60秒。值得注意的是:它的阻塞隊列是SynchronousQueue,這是一個沒有存儲性質的阻塞隊列,它的取值操做和放入操做必須是互斥的。根據源碼文檔的解釋,能夠理解爲每當有任務放入時會當即有線程將它取出執行。
3. ScheduledThreadPool 固定調度線程池
特色:有固定的核心線程,線程的數量沒有限制,默認存活時間爲60秒。同時支持定時及週期性任務執行。
4. SingleThreadExecutor 單核心線程池
特色:只有一個核心線程,因此能保證任務的串行化執行。
5. WorkStealingPool 並行執行線程池
特色:在jdk8中實現 線程池。它內部的線程池實現是ForkJoinPool,這是一個能夠同時利用多個線程來執行任務的線程池。無參默認使用CPU數量的線程數執行任務,因爲這個線程池比較複雜,下次專門寫一篇博文用於更新。
須要注意的是:線程池設計的流程是先利用核心線程處理、核心線程不能處理即把它放入阻塞隊列,最好才建立線程來執行任務,直到新建線程也失敗才調用拒絕服務處理。
試着理解一下這樣設計的好處。能夠看到,建立線程永遠不是最早想到的辦法,線程池儘可能避免建立線程。由於建立線程須要調用全局鎖來肯定線程的正確建立,同時也由於線程建立和銷燬也須要消耗資源,因此這種方式在最大努力的避免這種狀況的發生。
雖然在實際的開發中,線程池通常是隨着項目的部署一塊兒存活的,不會常常關閉,可是仍是須要了解如何關閉,怎麼關閉比較安全。
線程池可經過調用線程池的shutdown或shutdownNow方法來關閉線程池.
它們的原理是遍歷線程池中的工做線程,而後逐個調用線程的interrupt方法來中斷線程,因此沒法響應中斷的任務可能永遠沒法終止.
可是它們存在必定的區別
只要調用了這兩個關閉方法中的任意一個,isShutdown方法就會返回true.
當全部的任務都已關閉後,才表示線程池關閉成功,這時調用isTerminaed方法會返回true.
至於應該調用哪種方法,應該由提交到線程池的任務的特性決定,一般調用shutdown方法來關閉線程池,若任務不必定要執行完,則能夠調用shutdownNow方法.
線程關閉的方法轉載於做者:全網搜索關注JavaEdge
連接:https://www.nowcoder.com/discuss/152050?type=0&order=0&pos=6&page=0