線程池

什麼是線程池數據庫

線程池是一種多線程處理形式,處理過程當中將任務添加到隊列,而後在建立線程後自動啓動這些任務。線程池線程都是後臺線程。每一個線程都使用默認的堆棧大小,以默認的優先級運行,並處於多線程單元中。若是某個線程在託管代碼中空閒(如正在等待某個事件),則線程池將插入另外一個輔助線程來使全部處理器保持繁忙。若是全部線程池線程都始終保持繁忙,但隊列中包含掛起的工做,則線程池將在一段時間後建立另外一個輔助線程但線程的數目永遠不會超過最大值。超過最大值的線程能夠排隊,但他們要等到其餘線程完成後才啓動。緩存

爲何使用線程池安全

 使用線程池最大的緣由就是能夠根據系統的需求和硬件環境靈活的控制線程的數量,且能夠對全部線程進行統一的管理和控制,從而提升系統的運行效率,下降系統運行運行壓力;固然了,使用線程池的緣由不只僅只有這些,咱們能夠從線程池自身的優勢上來進一步瞭解線程池的好處;服務器

線程池的好處多線程

1:線程和任務分離,提高線程重用性;
2:控制線程併發數量,下降服務器壓力,統一管理全部線程;
3:提高系統響應速度,假如建立線程用的時間爲T1,執行任務用的時間爲T2,銷燬線程用的時間爲T3,那麼使用線程池就免去了T1和T3的時間;併發

ThreadPoolExecutor構造ide

 public ThreadPoolExecutor(int corePoolSize, // 1
                              int maximumPoolSize,  // 2
                              long keepAliveTime,  // 3
                              TimeUnit unit,  // 4
                              BlockingQueue<Runnable> workQueue, // 5
                              ThreadFactory threadFactory,  // 6
                              RejectedExecutionHandler handler ) { //7
        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 表示常駐核心線程數。若是等於0,則任務執行完成後,沒有任何請求進入時銷燬線程池的線程;若是大於0,即便本地任務執行完畢,核心線程也不會被銷燬。這個值的設置很是關鍵,設置過大會浪費資源,設置的太小會致使線程頻繁地建立或銷燬。
  • 第2個參數:maximumPoolSize 表示線程池可以容納同時執行的最大線程數。從上方的示例代碼中第一處來看,必須大於或等於1。若是待執行的線程數大於此值,須要藉助第5個參數的幫助。緩存在隊列中。若是maximumPoolSize 與corePoolSize 相等,便是固定大小線程池。
  • 第3個參數:keepAliveTime 表示線程池中的線程空閒時間,當空閒時間達到KeepAliveTime 值時,線程被銷燬,直到剩下corePoolSize 個線程爲止,避免浪費內存和句柄資源。在默認狀況下,當線程池的線程大於corePoolSize 時,keepAliveTime 纔會起做用。可是ThreadPoolExecutor的allowCoreThreadTimeOut 變量設置爲ture時,核心線程超時後也會被回收。
  • 第4個參數:TimeUnit 表示時間單位。keepAliveTime 的時間單位一般是TimeUnit.SECONDS。
  • 第5個參數:   workQueue 表示緩存隊列。當請求的線程數大於maximumPoolSize時,線程進入BlockingQueue 阻塞隊列。後續示例代碼中使用的LinkedBlockingQueue 是單向鏈表,使用鎖來控制入隊和出對的原子性,兩個鎖分別控制元素的添加和獲取,是一個生產消費模型隊列。
  • 第6個參數:threadFactory 表示線程工廠。它用來生產一組相同任務的線程。線程池的命名是經過給這個factory增長組名前綴來實現的。在虛擬機棧分析時,就能夠知道線程任務是由哪一個線程工廠產生的。
  • 第7個參數:handler 表示執行拒絕策略的對象。當超過第5個參數workQueue的任務緩存區上限的時候,就能夠經過該策略處理請求,這是一種簡單的限流保護。友好的拒絕策略可使以下三種:
  1. 保存到數據庫進行削峯填谷。在空閒的時候再拿出來執行。
  2. 轉向某個提示頁面。
  3. 打印日誌。

 快速理解其配置測試

咱們能夠經過下面的場景理解ThreadPoolExecutor中的各個參數;
a客戶(任務)去銀行(線程池)辦理業務,但銀行剛開始營業,窗口服務員還未就位(至關於線程池中初始線程數量爲0),
因而經理(線程池管理者)就安排1號工做人員(建立1號線程執行任務)接待a客戶(建立線程);
在a客戶業務還沒辦完時,b客戶(任務)又來了,因而經理(線程池管理者)就安排2號工做人員(建立2號線程執行任務)接待b客戶(又建立了一個新的線程);假設該銀行總共就2個窗口(核心線程數量是2);
緊接着在a,b客戶都沒有結束的狀況下c客戶來了,因而經理(線程池管理者)就安排c客戶先坐到銀行大廳的座位上(空位至關因而任務隊列)等候,並告知他: 若是一、2號工做人員空出,c就能夠前去辦理業務;
此時d客戶又到了銀行,(工做人員都在忙,大廳座位也滿了)因而經理趕忙安排臨時工(新建立的線程)在大堂站着,手持pad設備給d客戶辦理業務;
假如前面的業務都沒有結束的時候e客戶又來了,此時正式工做人員都上了,臨時工也上了,座位也滿了(臨時工加正式員工的總數量就是最大線程數),
因而經理只能按《超出銀行最大接待能力處理辦法》(飽和處理機制)拒接接待e客戶;
最後,進來辦業務的人少了,大廳的臨時工空閒時間也超過了1個小時(最大空閒時間),經理就會讓這部分空閒的員工人下班.(銷燬線程)
可是爲了保證銀行銀行正常工做(有一個allowCoreThreadTimeout變量控制是否容許銷燬核心線程,默認false),即便正式工閒着,也不得提早下班,因此一、2號工做人員繼續待着(池內保持核心線程數量);this

線程池工做流程圖spa

 

 自定義線程池

1:核心線程數(corePoolSize)
核心線程數的設計須要依據任務的處理時間和每秒產生的任務數量來肯定,例如:執行一個任務須要0.1秒,系統百分之80的時間每秒都會產生100個任務,那麼要想在1秒內處理完這100個任務,就須要10個線程,此時咱們就能夠設計核心線程數爲10;固然實際狀況不可能這麼平均,因此咱們通常按照8020原則設計便可,既按照百分之80的狀況設計核心線程數,剩下的百分之20能夠利用最大線程數處理;
2:任務隊列長度(workQueue)
任務隊列長度通常設計爲:核心線程數/單個任務執行時間*2便可;例如上面的場景中,核心線程數設計爲10,單個任務執行時間爲0.1秒,則隊列長度能夠設計爲200;

3:最大線程數(maximumPoolSize)
最大線程數的設計除了須要參照核心線程數的條件外,還須要參照系統每秒產生的最大任務數決定:例如:上述環境中,若是系統每秒最大產生的任務是1000個,那麼,最大線程數=(最大任務數-任務隊列長度)*單個任務執行時間;既: 最大線程數=(1000-200)*0.1=80個;
4:最大空閒時間(keepAliveTime)
這個參數的設計徹底參考系統運行環境和硬件壓力設定,沒有固定的參考值,用戶能夠根據經驗和系統產生任務的時間間隔合理設置一個值便可;

 說明:

1:編寫任務類(MyTask),實現Runnable接口;
2:編寫線程類(MyWorker),用於執行任務,須要持有全部任務;
3:編寫線程池類(MyThreadPool),包含提交任務,執行任務的能力;
4:編寫測試類(MyTest),建立線程池對象,提交多個任務測試;

/**
 * @author WGR
 * @create 2020/4/8 -- 20:00
 */
public class MyThreadPool {
    // 1:任務隊列   集合  須要控制線程安全問題
    private List<Runnable> tasks = Collections.synchronizedList(new LinkedList<>());
    //2:當前線程數量
    private int num;
    //3:核心線程數量
    private int corePoolSize;
    //4:最大線程數量
    private int maxSize;
    //5:任務隊列的長度
    private int workSize;

    public MyThreadPool(int corePoolSize, int maxSize, int workSize) {
        this.corePoolSize = corePoolSize;
        this.maxSize = maxSize;
        this.workSize = workSize;
    }

    //1:提交任務;
    public void submit(Runnable r){
        //判斷當前集合中任務的數量,是否超出了最大任務數量
        if(tasks.size()>=workSize){
            System.out.println("任務:"+r+"被丟棄了...");
        }else {
            tasks.add(r);
            //執行任務
            execTask(r);
        }
    }
    //2:執行任務;
    private void execTask(Runnable r) {
        //判斷當前線程池中的線程總數量,是否超出了核心數,
        if(num < corePoolSize){
            new MyWorker("核心線程:"+num,tasks).start();
            num++;
        }else if(num < maxSize){
            new MyWorker("非核心線程:"+num,tasks).start();
            num++;
        }else {
            System.out.println("任務:"+r+" 被緩存了...");
        }
    }
}
/**
 * @author WGR
 * @create 2020/4/8 -- 19:51
 */
public class MyTask implements Runnable{

    private int id;

    public MyTask(int id) {
        this.id = id;
    }

    @Override
    public void run() {
        String name = Thread.currentThread().getName();
        System.out.println("線程:"+name+" 即將執行任務:"+id);
        try {
            Thread.sleep(200);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("線程:"+name+" 完成了任務:"+id);
    }

    @Override
    public String toString() {
        return "MyTask{" +
                "id=" + id +
                '}';
    }
}
/**
 * @author WGR
 * @create 2020/4/8 -- 20:02
 */
public class MyTest {

    public static void main(String[] args) {
        //1:建立線程池類對象;
        MyThreadPool pool = new MyThreadPool(4,8,40);
        //2: 提交多個任務
        for (int i = 0; i <100 ; i++) {
            //3:建立任務對象,並提交給線程池
            MyTask my = new MyTask(i);
            pool.submit(my);
        }
    }
}
/**
 * @author WGR
 * @create 2020/4/8 -- 19:48
 */
public class MyWorker extends Thread {
    private String name;//保存線程的名字
    private List<Runnable> tasks;
    //利用構造方法,給成員變量賦值

    public MyWorker(String name, List<Runnable> tasks) {
        super(name);
        this.tasks = tasks;
    }

    @Override
    public void run() {
        //判斷集合中是否有任務,只要有,就一直執行任務
        while (tasks.size()>0){
            Runnable r = tasks.remove(0);
            r.run();
        }
    }

}

 

結果以下:

 

 

相關文章
相關標籤/搜索