併發編程-線程池-ThreadPoolExecutor機制

1、概述 java

一、ThreadPoolExecutor做爲java.util.concurrent包對外提供基礎實現,之內部線程池的形式對外提供管理任務執行,線程調度,線程池管理等等服務; 
二、Executors方法提供的線程服務,都是經過參數設置來實現不一樣的線程池機制。 
三、先來了解其線程池管理的機制,有助於正確使用,避免錯誤使用致使嚴重故障。同時能夠根據本身的需求實現本身的線程池
 數組

2、核心構造方法講解 異步

public ThreadPoolExecutor(int corePoolSize,  
                                          int maximumPoolSize,  
                                          long keepAliveTime,  
                                          TimeUnit unit,  
                                          BlockingQueue<Runnable> workQueue,  
                                          ThreadFactory threadFactory,  
                                          RejectedExecutionHandler handler) 線程

 

構造方法參數講解 code

參數名 做用
corePoolSize 核心線程池大小
maximumPoolSize 最大線程池大小
keepAliveTime 線程池中超過corePoolSize數目的空閒線程最大存活時間;能夠allowCoreThreadTimeOut(true)使得核心線程有效時間
TimeUnit keepAliveTime時間單位
workQueue 阻塞任務隊列
threadFactory 新建線程工廠
handler 當提交任務數超過maximumPoolSize+workQueue之和時,任務會交給RejectedExecutionHandler來處理

重點講解: 
其中比較容易讓人誤解的是:corePoolSize,maximumPoolSize,workQueue之間關係。 

1.當線程池小於corePoolSize時,新提交任務將建立一個新線程執行任務,即便此時線程池中存在空閒線程。 
2.當線程池達到corePoolSize時,新提交任務將被放入workQueue中,等待線程池中任務調度執行 
3.當workQueue已滿,且maximumPoolSize>corePoolSize時,新提交任務會建立新線程執行任務 
4.當提交任務數超過maximumPoolSize時,新提交任務由RejectedExecutionHandler處理 
5.當線程池中超過corePoolSize線程,空閒時間達到keepAliveTime時,關閉空閒線程 
6.當設置allowCoreThreadTimeOut(true)時,線程池中corePoolSize線程空閒時間達到keepAliveTime也將關閉 排序

線程管理機制圖示: 
隊列

 

workQueue 阻塞任務隊列it

  • ArrayBlockingQueue:  基於數組結構的有界隊列,此隊列按FIFO原則對任務進行排序。若是隊列滿了還有任務進來,則調用拒絕策略。
  • LinkedBlockingQueue:  基於鏈表結構的無界隊列,此隊列按FIFO原則對任務進行排序。由於它是無界的,根本不會滿,因此採用此隊列後線程池將忽略拒絕策略(handler)參數;同時還將忽略最大線程數(maximumPoolSize)等參數。
  • SynchronousQueue:   : 隊列長度爲1的隊列,和Array有點區別就是:client thread提交到block queue會是一個阻塞過程,直到有一個worker thread鏈接上來poll task。。每一個插入的操做必須等到另外一個調用移除的操做;若是新任務來了線程池沒有任何可用線程處理的話,則調用拒絕策略。其實要是把maximumPoolSize設置成無界(Integer.MAX_VALUE)的,加上SynchronousQueue隊列,就等同於Executors.newCachedThreadPool()。
  • PriorityBlockingQueue: 具備優先級的隊列的有界隊列,能夠自定義優先級;默認是按天然排序,可能不少場合並不合適。

handler 拒絕策略,當線程池與workQueue隊列都滿了的狀況下,對新加任務採起的策略。io

  • AbortPolicy:           拒絕任務,拋出RejectedExecutionException異常。默認值。
  • CallerRunsPolicy:   直接讓原先的client thread作爲worker線程,進行執行
  • DiscardOldestPolicy:  若是執行程序還沒有關閉,則位於工做隊列頭部的任務將被刪除,而後重試執行程序(若是再次失敗,則重複此過程)。這樣的結果是最後加入的任務反而有可能被執行到,先前加入的都被拋棄了。
  • DiscardPolicy:      加不進的任務都被拋棄了,同時沒有異常拋出。

3、Executors提供的線程池配置方案 

一、構造一個固定線程數目的線程池,配置的corePoolSize與maximumPoolSize大小相同,同時使用了一個無界LinkedBlockingQueue存放阻塞任務,所以多餘的任務將存在再阻塞隊列,不會由RejectedExecutionHandler處理 table

public static ExecutorService newFixedThreadPool(int nThreads) 

二、構造一個緩衝功能的線程池,配置corePoolSize=0,maximumPoolSize=Integer.MAX_VALUE,keepAliveTime=60s,以及一個無容量的阻塞隊列 SynchronousQueue,所以任務提交以後,將會建立新的線程執行;線程空閒超過60s將會銷燬 

public static ExecutorService newCachedThreadPool()

三、構造一個只支持一個線程的線程池,配置corePoolSize=maximumPoolSize=1,無界阻塞隊列LinkedBlockingQueue;保證任務由一個線程串行執行 

public static ExecutorService newSingleThreadExecutor() 

四、構造有定時功能的線程池,配置corePoolSize,無界延遲阻塞隊列DelayedWorkQueue;有意思的是:maximumPoolSize=Integer.MAX_VALUE,因爲DelayedWorkQueue是無界隊列,因此這個值是沒有意義的 

public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize)

總結: 
一、用ThreadPoolExecutor自定義線程池,看線程是的用途,若是任務量不大,能夠用無界隊列,若是任務量很是大,要用有界隊列,防止OOM  二、若是任務量很大,還要求每一個任務都處理成功,要對提交的任務進行阻塞提交,重寫拒絕機制,改成阻塞提交。保證不拋棄一個任務  三、最大線程數通常設爲2N+1最好,N是CPU核數  四、核心線程數,看應用,若是是任務,一天跑一次,設置爲0,合適,由於跑完就停掉了,若是是經常使用線程池,看任務量,是保留一個核心仍是幾個核心線程數  五、若是要獲取任務執行結果,用CompletionService,可是注意,獲取任務的結果的要從新開一個線程獲取,若是在主線程獲取,就要等任務都提交後才獲取,就會阻塞大量任務結果,隊列過大OOM,因此最好異步開個線程獲取結果

相關文章
相關標籤/搜索