什麼是線程池數據庫
線程池是一種多線程處理形式,處理過程當中將任務添加到隊列,而後在建立線程後自動啓動這些任務。線程池線程都是後臺線程。每一個線程都使用默認的堆棧大小,以默認的優先級運行,並處於多線程單元中。若是某個線程在託管代碼中空閒(如正在等待某個事件),則線程池將插入另外一個輔助線程來使全部處理器保持繁忙。若是全部線程池線程都始終保持繁忙,但隊列中包含掛起的工做,則線程池將在一段時間後建立另外一個輔助線程但線程的數目永遠不會超過最大值。超過最大值的線程能夠排隊,但他們要等到其餘線程完成後才啓動。緩存
爲何使用線程池安全
使用線程池最大的緣由就是能夠根據系統的需求和硬件環境靈活的控制線程的數量,且能夠對全部線程進行統一的管理和控制,從而提升系統的運行效率,下降系統運行運行壓力;固然了,使用線程池的緣由不只僅只有這些,咱們能夠從線程池自身的優勢上來進一步瞭解線程池的好處;服務器
線程池的好處多線程
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; }
快速理解其配置測試
咱們能夠經過下面的場景理解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(); } } }
結果以下: