並發編程從零開始(十四)-Executors工具類
12 Executors工具類
concurrent包提供了Executors工具類,利用它可以創建各種不同類型的線程池
12.1 四種對比
單線程的線程池:
固定數目線程的線程池:
每接收一個請求,就創建一個線程來執行:
單線程具有周期調度功能的線程池:
多線程,有調度功能的線程池:
12.2 最佳實踐
不同類型的線程池,其實都是由前面的幾個關鍵配置參數配置而成的。
在《阿里巴巴Java開發手冊》中,明確禁止使用Executors創建線程池,並要求開發者直接使用ThreadPoolExector或ScheduledThreadPoolExecutor進行創建。這樣做是為了強制開發者明確線程池的運行策略,使其對線程池的每個配置參數皆做到心中有數,以規避因使用不當而造成資源耗盡的風險。
13 ScheduledThreadPoolExecutor
ScheduledThreadPoolExecutor實現了按時間調度來執行任務:
1. 延遲執行任務
2. 周期執行任務
區別如下:
AtFixedRate:按固定頻率執行,與任務本身執行時間無關。但有個前提條件,任務執行時間必須小於間隔時間,例如間隔時間是5s,每5s執行一次任務,任務的執行時間必須小於5s。
WithFixedDelay:按固定間隔執行,與任務本身執行時間有關。例如,任務本身執行時間是10s,間隔2s,則下一次開始執行的時間就是12s。
13.1 延遲執行和周期性執行的原理
ScheduledThreadPoolExecutor繼承了ThreadPoolExecutor,這意味着其內部的數據結構和ThreadPoolExecutor是基本一樣的,那它是如何實現延遲執行任務和周期性執行任務的呢?
延遲執行任務依靠的是DelayQueue。DelayQueue是 BlockingQueue的一種,其實現原理是二叉堆。
而周期性執行任務是執行完一個任務之后,再把該任務扔回到任務隊列中,如此就可以對一個任務反復執行。
不過這里並沒有使用DelayQueue,而是在ScheduledThreadPoolExecutor內部又實現了一個特定的DelayQueue。
其原理和DelayQueue一樣,但針對任務的取消進行了優化。下面主要講延遲執行和周期性執行的實現過程。
13.2 延遲執行
傳進去的是一個Runnable,外加延遲時間delay。在內部通過decorateTask(...)方法把Runnable包裝成一個ScheduleFutureTask對象,而DelayedWorkQueue中存放的正是這種類型的對象,這種類型的對象一定實現了Delayed接口。
從上面的代碼中可以看出,schedule()方法本身很簡單,就是把提交的Runnable任務加上delay時間,轉換成ScheduledFutureTask對象,放入DelayedWorkerQueue中。任務的執行過程還是復用的ThreadPoolExecutor,延遲的控制是在DelayedWorkerQueue內部完成的。
13.4 執行周期
和schedule(...)方法的框架基本一樣,也是包裝一個ScheduledFutureTask對象,只是在延遲時間參數之外多了一個周期參數,然后放入DelayedWorkerQueue就結束了。
兩個方法的區別在於一個傳入的周期是一個負數,另一個傳入的周期是一個正數,為什么要這樣做呢?
用於生成任務序列號的sequencer,創建ScheduledFutureTask的時候使用:
withFixedDelay和atFixedRate的區別就體現在setNextRunTime里面。
如果是atFixedRate,period>0,下一次開始執行時間等於上一次開始執行時間+period;
如果是withFixedDelay,period < 0,下一次開始執行時間等於triggerTime(-p),為now+(-period),now即上一次執行的結束時間。