線程池的做用

線程池的做用

  1. 減小資源的開銷 
    減小了每次建立線程、銷燬線程的開銷。
  2. 提升響應速度 
    每次請求到來時,因爲線程的建立已經完成,故能夠直接執行任務,所以提升了響應速度。
  3. 提升線程的可管理性 
    線程是一種稀缺資源,若不加以限制,不只會佔用大量資源,並且會影響系統的穩定性。 
    所以,線程池能夠對線程的建立與中止、線程數量等等因素加以控制,使得線程在一種可控的範圍內運行,不只能保證系統穩定運行,並且方便性能調優。

 

線程池的實現原理

線程池通常由兩種角色構成:多個工做線程 和 一個阻塞隊列。數組

  • 工做線程 
    工做線程是一組已經處在運行中的線程,它們不斷地向阻塞隊列中領取任務執行。
  • 阻塞隊列 
    阻塞隊列用於存儲工做線程來不及處理的任務。當工做線程都在執行任務時,到來的新任務就只能暫時在阻塞隊列中存儲。

 

ThreadPoolExecutor的使用

建立線程池

經過以下代碼便可建立一個線程池:框架

new ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, timeUnit, runnableTaskQueue, handler);
  • corePoolSize:基本線程數量 
    它表示你但願線程池達到的一個值。線程池會盡可能把實際線程數量保持在這個值上下。
  • maximumPoolSize:最大線程數量 
    這是線程數量的上界。 
    若是實際線程數量達到這個值:
  1. 阻塞隊列未滿:任務存入阻塞隊列等待執行
  2. 阻塞隊列已滿:調用飽和策略
  • keepAliveTime:空閒線程的存活時間 
    當實際線程數量超過corePoolSize時,若線程空閒的時間超過該值,就會被中止。 
    PS:當任務不少,且任務執行時間很短的狀況下,能夠將該值調大,提升線程利用率。
  • timeUnit:keepAliveTime的單位
  • runnableTaskQueue:任務隊列 
    這是一個存聽任務的阻塞隊列,能夠有以下幾種選擇:
  1. ArrayBlockingQueue 
    它是一個由數組實現的阻塞隊列,FIFO。
  2. LinkedBlockingQueue 
    它是一個由鏈表實現的阻塞隊列,FIFO。 
    吞吐量一般要高於ArrayBlockingQueue。 
    fixedThreadPool使用的阻塞隊列就是它。 
    它是一個無界隊列。
  3. SynchronousQueue 
    它是一個沒有存儲空間的阻塞隊列,任務提交給它以後必需要交給一條工做線程處理;若是當前沒有空閒的工做線程,則當即建立一條新的工做線程。 
    cachedThreadPool用的阻塞隊列就是它。 
    它是一個無界隊列。
  4. PriorityBlockingQueue 
    它是一個優先權阻塞隊列。
  • handler:飽和策略 
    當實際線程數達到maximumPoolSize,而且阻塞隊列已滿時,就會調用飽和策略。 
    JDK1.5由四種飽和策略:
  1. AbortPolicy 
    默認。直接拋異常。
  2. CallerRunsPolicy 
    只用調用者所在的線程執行任務。
  3. DiscardOldestPolicy 
    丟棄任務隊列中最久的任務。
  4. DiscardPolicy 
    丟棄當前任務。

 

提交任務

能夠向ThreadPoolExecutor提交兩種任務:Callable和Runnable。異步

  1. Callable 
    該類任務有返回結果,能夠拋出異常。 
    經過submit函數提交,返回Future對象。 
    可經過get獲取執行結果。
  2. Runnable 
    該類任務只執行,沒法獲取返回結果,並在執行過程當中沒法拋異常。 
    經過execute提交。

 

關閉線程池

關閉線程池有兩種方式:shutdown和shutdownNow,關閉時,會遍歷全部的線程,調用它們的interrupt函數中斷線程。但這兩種方式對於正在執行的線程處理方式不一樣。函數

  1. shutdown() 
    僅中止阻塞隊列中等待的線程,那些正在執行的線程就會讓他們執行結束。
  2. shutdownNow() 
    不只會中止阻塞隊列中的線程,並且會中止正在執行的線程。

 

ThreadPoolExecutor運行機制

當有請求到來時:性能

  1. 若當前實際線程數量 少於 corePoolSize,即便有空閒線程,也會建立一個新的工做線程;
  2. 若當前實際線程數量處於corePoolSize和maximumPoolSize之間,而且阻塞隊列沒滿,則任務將被放入阻塞隊列中等待執行;
  3. 若當前實際線程數量 小於 maximumPoolSize,但阻塞隊列已滿,則直接建立新線程處理任務;
  4. 若當前實際線程數量已經達到maximumPoolSize,而且阻塞隊列已滿,則使用飽和策略。

 

設置合理的線程池大小

任務通常可分爲:CPU密集型、IO密集型、混合型,對於不一樣類型的任務須要分配不一樣大小的線程池。spa

  • CPU密集型任務 
    儘可能使用較小的線程池,通常爲CPU核心數+1。 
    由於CPU密集型任務使得CPU使用率很高,若開過多的線程數,只能增長上下文切換的次數,所以會帶來額外的開銷。
  • IO密集型任務 
    可使用稍大的線程池,通常爲2*CPU核心數。 
    IO密集型任務CPU使用率並不高,所以可讓CPU在等待IO的時候去處理別的任務,充分利用CPU時間。
  • 混合型任務 
    能夠將任務分紅IO密集型和CPU密集型任務,而後分別用不一樣的線程池去處理。 
    只要分完以後兩個任務的執行時間相差不大,那麼就會比串行執行來的高效。 
    由於若是劃分以後兩個任務執行時間相差甚遠,那麼先執行完的任務就要等後執行完的任務,最終的時間仍然取決於後執行完的任務,並且還要加上任務拆分與合併的開銷,得不償失。

 

Executor兩級調度模型

在HotSpot虛擬機中,Java中的線程將會被一一映射爲操做系統的線程。 
在Java虛擬機層面,用戶將多個任務提交給Executor框架,Executor負責分配線程執行它們; 
在操做系統層面,操做系統再將這些線程分配給處理器執行。操作系統

 

Executor結構

Executor框架中的全部類能夠分紅三類:線程

  1. 任務 
    任務有兩種類型:Runnable和Callable。
  2. 任務執行器 
    Executor框架最核心的接口是Executor,它表示任務的執行器。 
    Executor的子接口爲ExecutorService。 
    ExecutorService有兩大實現類:ThreadPoolExecutor和ScheduledThreadPoolExecutor。
  3. 執行結果 
    Future接口表示異步的執行結果,它的實現類爲FutureTask。

 

線程池

Executors工廠類能夠建立四種類型的線程池,經過Executors.newXXX便可建立。3d

1. FixedThreadPoolcode

public static ExecutorService newFixedThreadPool(int nThreads){
    return new ThreadPoolExecutor(nThreads,nThreads,0L,TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>());
}

 

  • 它是一種固定大小的線程池;
  • corePoolSize和maximunPoolSize都爲用戶設定的線程數量nThreads;
  • keepAliveTime爲0,意味着一旦有多餘的空閒線程,就會被當即中止掉;但這裏keepAliveTime無效;
  • 阻塞隊列採用了LinkedBlockingQueue,它是一個無界隊列;
  • 因爲阻塞隊列是一個無界隊列,所以永遠不可能拒絕任務;
  • 因爲採用了無界隊列,實際線程數量將永遠維持在nThreads,所以maximumPoolSize和keepAliveTime將無效。

 

2. CachedThreadPool

public static ExecutorService newCachedThreadPool(){
    return new ThreadPoolExecutor(0,Integer.MAX_VALUE,60L,TimeUnit.MILLISECONDS,new SynchronousQueue<Runnable>());
}

 

  • 它是一個能夠無限擴大的線程池;
  • 它比較適合處理執行時間比較小的任務;
  • corePoolSize爲0,maximumPoolSize爲無限大,意味着線程數量能夠無限大;
  • keepAliveTime爲60S,意味着線程空閒時間超過60S就會被殺死;
  • 採用SynchronousQueue裝等待的任務,這個阻塞隊列沒有存儲空間,這意味着只要有請求到來,就必需要找到一條工做線程處理他,若是當前沒有空閒的線程,那麼就會再建立一條新的線程。

 

3. SingleThreadExecutor

public static ExecutorService newSingleThreadExecutor(){
    return new ThreadPoolExecutor(1,1,0L,TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>());
}

 

  • 它只會建立一條工做線程處理任務;
  • 採用的阻塞隊列爲LinkedBlockingQueue;

 

4. ScheduledThreadPool

它用來處理延時任務或定時任務。

  • 它接收SchduledFutureTask類型的任務,有兩種提交任務的方式:
  1. scheduledAtFixedRate
  2. scheduledWithFixedDelay
  • SchduledFutureTask接收的參數:
  1. time:任務開始的時間
  2. sequenceNumber:任務的序號
  3. period:任務執行的時間間隔
  • 它採用DelayQueue存儲等待的任務
  • DelayQueue內部封裝了一個PriorityQueue,它會根據time的前後時間排序,若time相同則根據sequenceNumber排序;
  • DelayQueue也是一個無界隊列;
  • 工做線程的執行過程:
  • 工做線程會從DelayQueue取已經到期的任務去執行;
  • 執行結束後從新設置任務的到期時間,再次放回DelayQueue
相關文章
相關標籤/搜索