Executor框架能夠將任務的提交與任務的執行策略解耦開來。 並不是全部的任務都使用全部的執行策略,有些任務須要明確的指定執行策略,包括:安全
依賴性任務:提交給線程池的任務須要依賴其餘的任務,那麼就隱含地給執行策略帶來了約束,此時必須當心地維持這些執行策略以免產生活躍性問題併發
使用線程封閉機制的任務:單線程的Executor可以對併發性作出更強的承諾,對象能夠封閉在任務線程中,使得在該線程中執行的任務在訪問該對象時不須要同步,即便這些資源不是線程安全的也沒有問題。但這種情形將在任務與執行策略之間造成隱式的耦合----任務要求其執行所在的Executor是單線程的。框架
對響應時間敏感的任務函數
使用ThreadLocal的任務:只有當線程本地值的生命週期受限於任務的生命週期時,在線程池的線程中使用ThreadLocal纔有意義,而在線程池的線程中不該該使用ThreadLocal在任務之間傳遞值。性能
只有當任務都是同類型的而且相互獨立時,線程池的性能才能達到最佳。
線程池的理想大小取決於被提交任務的類型以及所部署系統的特性。在代碼中一般不會固定線程池的大小,而應該經過某種配置機制來提供,或者根據Runtime.availableProcessors來動態計算。線程
ThreadPoolExecutor是一個靈活的、穩定的線程池,容許進行各類定製。 若是默認的執行策略不能知足需求,那麼能夠經過ThreadPoolExecutor的構造函數來實例化一個對象,並根據本身的需求來定製。日誌
public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler) { ... }
ThreadPoolExecutor是可擴展的,它提供了幾個能夠在子類化中改寫的方法:beforeExecute、afterExecute和terminated,這些方法能夠用於擴展ThreadPoolExecutor的行爲。 在執行任務的線程中將調用beforeExecute和afterExecute等方法,在這些方法中還能夠添加日誌、計時、監視或統計信息收集的功能。 不管任務是從run中正常返回,仍是拋出一個異常而返回,afterExecute都會被調用,若是任務在完成後帶有一個Error,那麼就不會調用afterExecute。若是beforeExecute拋出一個RuntimeException,那麼任務將不被執行,而且afterExecute也不會被調用。在線程池完成關閉操做時調用terminated,也就是在全部任務都已經完成而且全部工做者線程已經關閉後。code
線程池的基本大小(Core Pool Size)、最大大小(Maximum Pool Size)以及存活時間等因素共同負責線程的建立與銷燬。對象
基本大小:線程池的目標大小,即在沒有任務執行時線程池的大小,而且只有在工做隊列滿了的狀況下才會建立超出這個數量的線程。生命週期
最大大小:可同時活動的線程數量的上限。
存活施加:若是某個線程的空閒時間超過了存活時間,那麼將被標記爲可回收的,而且當線程池的當前大小超過了基本大小時,這個線程被終止。
經過調節線程池的基本大小和存活時間,能夠幫助線程池禍首空閒線程佔有的資源,從而使得這些資源能夠用於執行其餘工做。
newFixedThreadPool工廠方法將線程池的基本大小和最大大小設置爲參數中指定的值,並且建立的線程池不會超時。 newCachedThreadPool工廠方法將線程池的最大大小設置爲Integer.MAX_VALUE,而將基本大小設置爲0,並將超時設置爲1分鐘。這種方法建立出來的線程池能夠被無限擴展,而且當需求下降時會自動收縮。
當新的任務請求的到達速率超過了線程池的處理速率,那麼新到來的請求將積累起來,在線程池中,這些請求會在一個由Executor管理的Runnable隊列中等待,而不會像線程那樣去競爭CPU資源。 ThreadPoolExecutor容許提供一個BlockingQueue來保存等待執行的任務,基本的任務排隊方法有3種:無界隊列、有界隊列和同步移交。 newFixedThreadPool和newSingleThreadExecutor在默認狀況下將使用一個無界的LinkedBlockingQueue。若是全部的工做者線程都處於忙碌狀態,那麼任務將在隊列中等候。若是任務持續快速地到達,而且超過了線程池處理它們的速度,那麼隊列將無限制地增長。 一種更穩妥的資源管理策略是使用有界隊列,例如ArrayBlockingQueue、有界的LinkedBlockingQueue、PriorityBlockingQueue。
在使用有界的工做隊列時,隊列的大小與線程池的大小必須一塊兒調節。若是線程池較小而隊列較大,那麼有助於減小內存使用量,下降CPU的使用率,同時還能夠減小上下文切換,但可能會限制吞吐量。對於很是大的或者無界的線程池,能夠經過使用SynchronousQueue來避免任務排隊,以及直接將任務從生產者移交給工做者線程。SynchronousQueue不是一個真正的隊列,而是一種在線程之間進行移交的機制,要將一個元素放入SynchronousQueue中,必須有另外一個線程正在等待接受這個元素,若是沒有線程正在等待,而且線程池的當前大小小於最大值,那麼ThreadPoolExecutor將建立一個新的線程,不然根據飽和策略,這個任務將被拒絕。
使用直接移交將更高效,由於任務會直接移交給執行它的線程,而不是被首先放在隊列中,而後由工做者線程從隊列中提取該任務。只有當線程池是無界的或者能夠拒絕任務時,SynchronousQueue纔有實際價值。 只有當任務相互獨立時,爲線程池或工做隊列設置界限纔是合理的,若是任務之間存在依賴性,那麼有界的線程池或隊列就可能致使線程「飢餓」死鎖問題,此時應該使用無界線程池,如newCachedThreadPool。
當有界隊列被填滿後,飽和策略開始發揮做用。ThreadPoolExecutor的飽和策略能夠經過調用setRejectedExecutionHandler來修改。JDK提供了幾種不一樣的飽和策略:
每當線程池須要建立一個線程時,都是經過線程工廠方法來完成的。默認的線程工廠方法將建立一個新的、非守護的線程,而且不包含特殊的配置信息,經過制定一個線程工廠方法,能夠定製線程池的配置信息。
//線程工廠原型 public interface ThreadFactory { Thread newThread(Runnable r); } //自定義的線程工廠 public class MyThreadFactory implements ThreadFactory { public Thread newThread(Runnable runnable) { ... } }