併發程式設計從零開始(十四)-Executors工具類

會程式設計的老六發表於2021-11-02

併發程式設計從零開始(十四)-Executors工具類

12 Executors工具類

concurrent包提供了Executors工具類,利用它可以建立各種不同型別的執行緒池

12.1 四種對比

單執行緒的執行緒池:

image-20211102170108915

固定數目執行緒的執行緒池:

image-20211102171055728

每接收一個請求,就建立一個執行緒來執行:

image-20211102172234191

單執行緒具有周期排程功能的執行緒池:

image-20211102172303690

多執行緒,有排程功能的執行緒池:

image-20211102172315063


12.2 最佳實踐

不同型別的執行緒池,其實都是由前面的幾個關鍵配置引數配置而成的。

在《阿里巴巴Java開發手冊》中,明確禁止使用Executors建立執行緒池,並要求開發者直接使用ThreadPoolExector或ScheduledThreadPoolExecutor進行建立。這樣做是為了強制開發者明確執行緒池的執行策略,使其對執行緒池的每個配置引數皆做到心中有數,以規避因使用不當而造成資源耗盡的風險。


13 ScheduledThreadPoolExecutor

ScheduledThreadPoolExecutor實現了按時間排程來執行任務:

1. 延遲執行任務

image-20211102181437787

image-20211102181444236

2. 週期執行任務

image-20211102181510358

image-20211102181516736

區別如下:

AtFixedRate:按固定頻率執行,與任務本身執行時間無關。但有個前提條件,任務執行時間必須小於間隔時間,例如間隔時間是5s,每5s執行一次任務,任務的執行時間必須小於5s。

WithFixedDelay:按固定間隔執行,與任務本身執行時間有關。例如,任務本身執行時間是10s,間隔2s,則下一次開始執行的時間就是12s。


13.1 延遲執行和週期性執行的原理

ScheduledThreadPoolExecutor繼承了ThreadPoolExecutor,這意味著其內部的資料結構和ThreadPoolExecutor是基本一樣的,那它是如何實現延遲執行任務和週期性執行任務的呢?

延遲執行任務依靠的是DelayQueue。DelayQueue是 BlockingQueue的一種,其實現原理是二叉堆。

而週期性執行任務是執行完一個任務之後,再把該任務扔回到任務佇列中,如此就可以對一個任務反覆執行。

不過這裡並沒有使用DelayQueue,而是在ScheduledThreadPoolExecutor內部又實現了一個特定的DelayQueue

image-20211102183205357

其原理和DelayQueue一樣,但針對任務的取消進行了優化。下面主要講延遲執行和週期性執行的實現過程。


13.2 延遲執行

image-20211102183251134

傳進去的是一個Runnable,外加延遲時間delay。在內部通過decorateTask(...)方法把Runnable包裝成一個ScheduleFutureTask物件,而DelayedWorkQueue中存放的正是這種型別的物件,這種型別的物件一定實現了Delayed介面。

image-20211102183302311

image-20211102183324118

從上面的程式碼中可以看出,schedule()方法本身很簡單,就是把提交的Runnable任務加上delay時間,轉換成ScheduledFutureTask物件,放入DelayedWorkerQueue中。任務的執行過程還是複用的ThreadPoolExecutor,延遲的控制是在DelayedWorkerQueue內部完成的。


13.4 執行週期

image-20211102183354946

image-20211102183433106

和schedule(...)方法的框架基本一樣,也是包裝一個ScheduledFutureTask物件,只是在延遲時間引數之外多了一個週期引數,然後放入DelayedWorkerQueue就結束了。

兩個方法的區別在於一個傳入的週期是一個負數,另一個傳入的週期是一個正數,為什麼要這樣做呢?

用於生成任務序列號的sequencer,建立ScheduledFutureTask的時候使用:

image-20211102183456120

image-20211102184926446

image-20211102184939941

withFixedDelay和atFixedRate的區別就體現在setNextRunTime裡面。

如果是atFixedRate,period>0,下一次開始執行時間等於上一次開始執行時間+period;

如果是withFixedDelay,period < 0,下一次開始執行時間等於triggerTime(-p),為now+(-period),now即上一次執行的結束時間。

相關文章