線程池原理和實現

$1. 概述

線程池產生背景:高併發項目中常常須要同時啓用大量線程,所以須要建立大量線程,而頻繁的建立新線程和殺死舊線程將會大大拖慢CPU的性能,所以利用線程池一次性初始化若干線程,使用時從池中彈出線程,執行完線程池再予以回收java

線程池的工做流程:主要是控制運行的線程數量,處理過程當中將任務放入隊列,而後在線程建立後啓動這些任務,若是線程數量超過了最大數量,超出數量的線程排隊等候,等其餘線程執行完畢,再從隊列中取出任務執行編程

線程池的主要特色:實現線程複用、控制最大併發數;可以系統地管理線程併發

線程池的優點ide

  • 下降資源消耗,經過重複利用已建立的線程下降線程頻繁建立和銷燬的損耗
  • 提升響應速度,當任務到達時,任務能夠不須要等待線程建立就能當即執行
  • 提升線程的可管理性,線程池能夠對線程進行統一的分配調度、調優以及監控

$2. 線程池的實現方式

2.1 經過Executors工具類

  • Executors.newFixedThreadPool(int nThreads):固定線程數量線程池
// 源碼  
public static ExecutorService newFixedThreadPool(int nThreads) {  
 return new ThreadPoolExecutor(nThreads, nThreads,  
                               0L, TimeUnit.MILLISECONDS,  
                               new LinkedBlockingQueue<Runnable\>());  
}
  • Executors.newSingleThreadExecutor():單線程線程池
// 源碼  
public static ExecutorService newSingleThreadExecutor() {  
 return new FinalizableDelegatedExecutorService  
 (new ThreadPoolExecutor(1, 1,  
                         0L, TimeUnit.MILLISECONDS,  
                         new LinkedBlockingQueue<Runnable\>()));  
}
  • Executors.newCachedThreadExecutor():可擴容線程池,理論上支持Integer.MAX_VALUE條線程
// 源碼
public static ExecutorService newCachedThreadPool() {  
 return new ThreadPoolExecutor(0, Integer.MAX\_VALUE,  
                               60L, TimeUnit.SECONDS,  
                               new SynchronousQueue<Runnable\>());  
}

實質上,這三種線程池雖然各有特色,但底層實現的都是ThreadPoolExecutor類,也即最正統的線程池,線程池本池☺高併發

  • 實際使用
// 使用案例
import java.util.concurrent.Executors;

public class NewFixedThreadPoolDemo {
    public static void main(String[] args) {
        ExecutorService threadPool = Executors.newFixedThreadPool(5);
        // ExecutorService threadPool = Executors.newSingleThreadExecutor();
        // ExecutorService threadPool = Executors.newCachedThreadExecutor();
        
        
        for(int i=0; i<10; i++){
            threadPool.execute(()-> System.out.println(Thread.currentThread().getName() + "\t 辦理業務"));
        }
        
        threadPool.shutdown(); // 關閉資源!!
    }
}

2.2 經過ThreadPoolExecutor[線程池本池!用它!用它! 用它!]

WHY?(誰說的不能用Executors?)

  • 馬雲曰:我說的!(阿里編程規範明確表示不建議內部使用Executors工具類實現線程池)
  • fixThreadPool和SingleThreadPool設置的任務請求隊列爲Integer.MAX_VALUE,可能會堆積大量的請求,從而致使OOM
  • CachedThreadPool和ScheduledThreadPool容許建立的線程數量爲Integer.MAX_VALUE, 可能會建立大量線程,從而致使OOM
// 代碼實現
import java.util.concurrent.*;
public class ThreadPoolExecutorDemo {
    public static void main(String[] args) {
        ExecutorService threadPool = new ThreadPoolExecutor(2,
                5,
                2L,
                TimeUnit.SECONDS,
                new LinkedBlockingDeque<>(3),
                Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.DiscardPolicy());

        for(int i=1; i<=10; i++){
            final int temp = i;
            threadPool.execute(()-> System.out.println(Thread.currentThread().getName() + "\t辦理業務" + temp));
        }
        threadPool.shutdown();
    }
}

參數解析

  • corePoolSize:線程池中的常駐核心線程數
  • maximumPoolSize:線程池中可以容納同時執行的最大線程數,該值必須大於等於1
  • keepAliveTime:多餘的空閒線程的存貨時間,當前池中線程數量大於corePoolSize時,當空閒線程的空閒時間達到keepAliveTime,多餘的線程會被銷燬直到剩下corePoolSize個
  • unit:keepAliveTime的單位
  • workQueue:任務隊列,被提交但還沒有被執行的任務 -- 想象銀行的等候區
  • threadFactory:表示生成線程池中工做線程的線程工廠,用於建立線程,通常使用默認便可
  • handler:拒絕策略,表示當線程池和隊列都滿了,線程池採起何種策略來拒絕請求執行的Runnable任務

$3. 線程池工做原理

talk is cheap, show me the diagram工具

線程池工做原理.PNG

線程池通常時間都只會開啓核心線程數量的線程,而當任務隊列滿了以後,線程池會啓用非核心線程池區(這裏描述只是邏輯分區,實際上線程之間都是同等地位),建立線程並執行任務;而當任務隊列中任務變少,有些線程開始閒置,閒置時間達到設置的keepAliveTime後,線程池會註銷回收這些閒置線程,直到線程數量恢復核心線程數性能

  • 一個線程池可以併發處理的最大任務數 = maximumPoolSize + workQueue設置的長度

$4. 拒絕策略(渣男[女]必備)

共有四種拒絕策略,以2.2中的代碼爲準,即設置核心線程數爲二、最大線程數爲五、任務隊列容量爲三、任務數爲10;見四種策略分別的運行結果以下spa

  • AbortPolicy:當沒法執行任務時,直接拋出RejectedExecutionException中斷程序

7.PNG

  • CallerRunsPolicy:調用者運行機制,該策略不會拋棄任務,也不會拋出異常,而是將沒法執行的任務返還給調用者執行,這裏由於是main線程調用的,所以返還給main線程執行任務。以下圖,任務9被返還給main線程執行了,這過程的時間內線程池可能已經執行完任務1了,任務隊列從而空了一個位置,任務10便直接進入任務隊列沒有返還給main線程。在保證任務徹底性的狀況下,該策略是惟一策略

8.PNG

  • DiscardOldestPolicy:拋棄隊列中等待最久的任務,而後把當前任務加入到隊列中嘗試再次提交當前任務

9.PNG

  • DiscardPolicy:該策略會丟棄沒法處理的任務,不作任何處理也不拋異常,若是容許任務丟失,這是最好的一種策略

10.PNG

REFERENCE

尚硅谷周陽老師JUC課程https://www.bilibili.com/vide...線程

相關文章
相關標籤/搜索