Java 併發框架中的線程池 thread pool 爲啥不是直接創建 maximumPoolSize 個線程之後,再把任務丟到隊列中?

在開發過程中,合理使用線程池,可以有以下好處。

1,降低資源消耗;提高線程到重發利用率,降低創建和銷燬線程的消耗。

2,提高響應速度;任務來了,直接有線程可用可執行,而不是先創建線程,再執行。

3,提高線程的可管理性;線程是稀缺資源,使用線程池可以統一分配調優監控。

在使用Java的線程池的時候,都是使用同一個底層方法來創建線程的

    public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler) {
        //.........
    }

下面解釋下一下構造器中各個參數的含義:

  • corePoolSize:核心池的大小。在創建了線程池後,默認情況下,線程池中並沒有任何線程,而是等待有任務到來才創建線程去執行任務,除非調用了prestartAllCoreThreads()或者prestartCoreThread()方法,從這2個方法的名字就可以看出,是預創建線程的意思,即在沒有任務到來之前就創建corePoolSize個線程或者一個線程。默認情況下,在創建了線程池後,線程池中的線程數爲0,當有任務來之後,就會創建一個線程去執行任務,當線程池中的線程數目達到corePoolSize後,就會把到達的任務放到緩存隊列當中;
  • maximumPoolSize:線程池最大線程數,這個參數也是一個非常重要的參數,它表示在線程池中最多能創建多少個線程;
  • keepAliveTime:表示線程沒有任務執行時最多保持多久時間會終止。默認情況下,只有當線程池中的線程數大於corePoolSize時,keepAliveTime纔會起作用,直到線程池中的線程數不大於corePoolSize,即當線程池中的線程數大於corePoolSize時,如果一個線程空閒的時間達到keepAliveTime,則會終止,直到線程池中的線程數不超過corePoolSize。但是如果調用了allowCoreThreadTimeOut(boolean)方法,在線程池中的線程數不大於corePoolSize時,keepAliveTime參數也會起作用,直到線程池中的線程數爲0;
  • unit:參數keepAliveTime的時間單位,有7種取值,不列了。
  • workQueue:一個阻塞隊列,用來存儲等待執行的任務,這個參數的選擇也很重要,會對線程池的運行過程產生重大影響,一般來說,這裏的阻塞隊列有以下幾種選擇:
ArrayBlockingQueue;
LinkedBlockingQueue;
SynchronousQueue;

  ArrayBlockingQueue和PriorityBlockingQueue使用較少,一般使用LinkedBlockingQueue和Synchronous。線程池的排隊策略與BlockingQueue有關。

  • threadFactory:線程工廠,主要用來創建線程;
  • handler:表示當拒絕處理任務時的策略,有以下四種取值:
ThreadPoolExecutor.AbortPolicy:丟棄任務並拋出RejectedExecutionException異常。 
ThreadPoolExecutor.DiscardPolicy:也是丟棄任務,但是不拋出異常。 
ThreadPoolExecutor.DiscardOldestPolicy:丟棄隊列最前面的任務,然後重新嘗試執行任務(重複此過程)
ThreadPoolExecutor.CallerRunsPolicy:由調用線程處理該任務 

ThreadPoolExecutor.execute()分四種情況

1),若當前運行的線程小於corePoolSize,則創建新線程執行任務。(執行此步驟要獲取全局鎖)

2),若運行線程數>=corePoolSize,則將任務入隊列。

3),若無法入隊列(滿來),則創建新線程執行任務。(執行此步驟要獲取全局鎖)

4),若創建新線程使得當前運行線程數>maximumPoolSize,任務將拒絕。

問題來了:爲啥不直接創建線程,一直到線程數>=maximumPoolSize的時候,再將任務入到隊列,等待執行呢?

在看別人的文章說明的時候,我也真有這麼個問題,爲啥不呢?

下面是自己說服自己的理由。

1),首先,上面說了,在創建新線程的時候,是要獲取全局鎖的,這個時候其它的就得阻塞,影響了整體效率。

2),上面的原理還說,大於corePoolSize的線程在沒工作的時候,超時是會被銷燬的。

這麼不好理解的話,舉個例子。

就好比一個國企裏面有10個(core)正式工的名額,最多招10個正式工,要是任務超過正式工人數(task > core)的情況下,工廠領導(線程池)不是首先擴招工人,還是這10人,但是任務可以稍微積壓一下,即先放到隊列去(代價低)。10個正式工慢慢幹,遲早會幹完的,要是任務還在繼續增加,超過正式工的加班忍耐極限了(隊列滿了),就的招臨時工幫忙了(注意是臨時工)要是正式工加上臨時工還是不能完成任務,那新來的任務就會被領導拒絕了(線程池的拒絕策略)。

爲啥老闆不上來直接招正式工?

首先,招人(new thread)是不是得走流程,辦手續,耗時耗力;

其次,要是任務量降低了,養多出來的幾個人沒活幹成本高(維護更多的線程的成本);

最後,要辭退臨時工也得各種走流程,各種賠償,得不償失。(超時清掉產能過剩的線程的成本)。

正式工(線程)相對於計算機世界,是稀缺資源。國企的鐵飯碗可不就是稀缺資源嘛,不到不得已,是不能氾濫的。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章