在咱們平時本身寫線程的測試demo時,通常都是用new Thread的方式來建立線程。可是,咱們知道建立線程對象,就會在內存中開闢空間,而線程中的任務執行完畢以後,就會銷燬。程序員
單個線程的話還好,若是線程的併發數量上來以後,就會頻繁的建立和銷燬對象。這樣,勢必會消耗大量的系統資源,進而影響執行效率。segmentfault
因此,線程池就應運而生。併發
能夠經過idea先看下線程池的類圖,瞭解一下它的繼承關係和大概結構。ide
它繼承自AbstractExecutorService類,這是一個抽象類,不過裏邊的方法都是已經實現好的。而後這個類實現了ExecutorService接口,裏邊聲明瞭各類方法,包括關閉線程池,以及線程池是否已經終止等。此接口繼承自父接口Executor,裏邊只聲明瞭一個execute方法。函數
線程池就是爲了解決單個線程頻繁的建立和銷燬帶來的性能開銷。同時,能夠幫咱們自動管理線程。而且不須要每次執行新任務都去建立新的線程,而是重複利用已有的線程,大大提升任務執行效率。性能
咱們打開 ThreadPoolExecutor的源碼,能夠看到總共有四個構造函數。學習
可是,前三個最終都會調用到最後一個構造函數。咱們來看下這個構造函數都有哪些參數。(其實,多看下參數的英文解釋就能明白其中的含義,看來英語對程序員來講是真的重要呀)測試
//核心構造函數 public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler) { if (corePoolSize < 0 || maximumPoolSize <= 0 || maximumPoolSize < corePoolSize || keepAliveTime < 0) throw new IllegalArgumentException(); if (workQueue == null || threadFactory == null || handler == null) throw new NullPointerException(); this.corePoolSize = corePoolSize; this.maximumPoolSize = maximumPoolSize; this.workQueue = workQueue; this.keepAliveTime = unit.toNanos(keepAliveTime); this.threadFactory = threadFactory; this.handler = handler; }
1)corePoolSize字體
表明核心線程數。每當新的任務提交過來的時候,線程池就會建立一個核心線程來執行這個任務,即便已經有其餘的核心線程處於空閒狀態。 而當須要執行的任務數大於核心線程數時,將再也不建立新的核心線程。this
其實,咱們能夠看下JDK提供的官方註釋說明。even if they are idle,就照應上邊的加粗字體。
此外,最後一句話說,除非allowCoreThreadTimeOut 這個參數被設置了值。
什麼意思呢,能夠去看下這個參數默認值是false,表明當核心線程在空閒狀態時,即沒有任務在執行,就會一直存活,不會銷燬。而設置爲true以後,就會有線程存活時間,即假如設置存活時間60秒,則60秒以後,若是沒有新的可執行任務,則核心線程也會自動銷燬。
2)maximumPoolSize
線程所容許的最大數量。即,當阻塞隊列已滿的時候,而且已經建立的線程數小於最大線程數,則會建立新的線程去執行任務。因此,這個參數只有在阻塞隊列滿的狀況下才有意義。所以,對於無界隊列,這個參數將會失去效果。
3)keepAliveTime
表明線程空閒後,保持存活的時間。也就是說,超過必定的時間沒有任務執行,線程就會自動銷燬。
注意,這個參數,是針對大於核心線程數,小於最大線程數的那部分非核心線程來講的。若是是任務數量特別多的狀況下,能夠適當增長這個參數值的大小。以保證,在下個任務到來以前,此線程不會當即銷燬,從而避免線程的從新建立。
4)unit
這個是描述存活時間的時間單位。可使用TimeUnit裏邊的枚舉值。
5)workQueue
表明阻塞隊列,存儲全部等待執行的任務。
6)threadFactory
表明用來建立線程的工廠。能夠自定義一個工廠,傳參進來。若是不指定的話,就會使用默認工廠(Executors類裏邊的 DefaultThreadFactory)。
能夠看到,會給每一個線程的名字指定一個有規律的前綴。而且每一個線程都設置相同的優先級(優先級總共有三個,一、五、10)。優先級能夠理解爲,優先級高的線程被執行的機率會更高,可是不表明優先級高的線程必定會先執行。
7)handler
這個參數表明,拒絕策略。當阻塞隊列和線程池都滿了,即達到了最大線程數,會用什麼策略來處理。一共有四種策略可供選擇,分別對應四個內部類。
總結一下線程池的執行過程。
咱們通常用 execute 方法來提交任務給線程池。當線程須要返回值時,可使用submit 方法。
shutdown方法用來關閉線程池。注意,此時再也不接受新提交的任務,可是,會繼續處理正在運行的任務和阻塞隊列裏邊的任務。
shutdownNow也會關閉線程池。可是,它再也不接受新任務,而且會嘗試終止正在運行的任務。
瞭解了線程池工做流程以後,那麼咱們怎樣去建立它呢。
Executors類提供了四種經常使用的方法。能夠發現它們最終都調用了線程池的構造方法。都有兩種建立方式,其中一種能夠傳自定義的線程工廠。此處,只貼出不帶工廠的方法便於理解。
①newFixedThreadPool
public static ExecutorService newFixedThreadPool(int nThreads) { return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>()); }
建立一個固定大小的線程池。核心線程數和最大線程數相等。當線程數量達到核心線程數時,新任務就會放到阻塞隊列裏邊等待執行。
②newSingleThreadExecutor
public static ExecutorService newSingleThreadExecutor() { return new FinalizableDelegatedExecutorService (new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>())); }
建立一個核心線程數和最大線程數都是1的線程池。即線程池中只會存在一個正在執行的線程,若線程空閒則執行,不然把任務放到阻塞隊列。
③ newCachedThreadPool
public static ExecutorService newCachedThreadPool() { return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>()); }
建立一個可根據實際狀況調整線程個數的線程池。這句話,能夠理解爲,有多少任務同時進來,就會建立同等數量的線程去執行任務。固然,這是在線程數不能超過Integer最大值的前提下。
當再來一個新任務時,如有空閒線程則執行任務。不然,等線程空閒60秒以後,就會自動回收。
當沒有新任務,就不會建立新的線程。
④newScheduledThreadPool
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) { return new ScheduledThreadPoolExecutor(corePoolSize); } public ScheduledThreadPoolExecutor(int corePoolSize) { super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS, new DelayedWorkQueue()); }
建立一個可指定核心線程數的線程池。這個線程池能夠執行週期性的任務。
若是本文對你有用,歡迎點贊,評論,轉發。
學習是枯燥的,也是有趣的。我是「煙雨星空」,歡迎關注,可第一時間接收文章推送。