圖解定時任務執行緒池
執行緒池概念
我們上篇文章分析了ThreadPoolExecutor,如果要用一句話說明它的主要優勢,就是執行緒置換。還有Executors工具類,極大的簡化了研發人員工作。
我用一個圖重複描述下執行緒池概念。多生產-多消費模型。
- 生產者將執行緒任務丟進執行緒池中,生產者就就結束了。
- 執行緒池控制消費者消費元素,消費者可以是1個或者多個,取決於執行緒池引數corePoolSize和maxPoolSize設定。
- 阻塞佇列是用來裝生產者丟進去的執行緒任務,如ArrayBlockingQueue,LinkedBlockingQueue,DelayedQueue等。如果生產者生產能力超過消費者消費能力,如果阻塞佇列有長度限制並且超過佇列長度執行緒池會執行飽和策略,如果佇列沒有長度限制,可也能出現OOM哦,因為執行緒任務可能把記憶體都撐爆了,這也是面試常考點哦!
詳細概念可以翻看我上一篇文章《執行緒池面試必考問題》。
定時任務延時原理
還記得我們上面說的阻塞佇列嗎?定時任務執行緒池底層使用DelayedQueue實現的,這種延遲佇列有一個最大的特點:按時出佇列,大家都考過駕照吧,科目三考試的時候都是車上坐的是4個人,假設一個人考試需要花15分鐘,那麼考試學員佇列看起來是這樣的。
DelayedQueue底層需要實現Delayed介面同時需要實現getDelay方法和compareTo方法,getDelay方法用於計算出佇列時間,一旦小於0就會出佇列;compareTo方法用於按觸發時間從小到大排序。這就是Schedule執行緒池任務延時原理,如果需要看案例程式碼,請參考我文章《併發佇列:PriorityBlockingQueue和DelayQueue案例使用》。
scheduleWithFixedDelay和scheduleAtFixedRate區別
由上圖可知:假設執行緒任務:耗時1秒,定時3秒執行,scheduleWithFixedDelay其實是4秒執行一次。
- scheduleWithFixedDelay:是以任務結束時間週期執行。
- scheduleAtFixedRate:是以固定週期執行。
FutureTask獲取返回值
在ScheduledThreadPoolExecutor中,ScheduledFutureTask是獲取定時任務返回值,繼承FutureTask。我們看下FutureTask呼叫get阻塞簡化流程圖。
- 向執行緒池新增任務,任務被封裝成ScheduledFutureTask並且實現Callable介面是為了獲取任務返回值。
- 當客戶端執行緒通過get方式獲取執行緒池執行緒執行的返回值,客戶端執行緒會阻塞直到執行緒池執行緒任務執行完。
在實際運用,我們一般拿返回值測試多執行緒效能。
Timer比較
- Timer是單執行緒,而且不帶返回值。ScheduledThreadPoolExecutor是多執行緒的,採用執行緒複用代替建立新的執行緒,並且FutureTask帶返回值。
- Timer執行緒呼叫sche方法,如果TimerTask出異常,Timer單執行緒直接掛掉退出,而ScheduledThreadPoolExecutor會捕獲了異常,不影響其他消費者執行緒。下面是程式碼測試。
import java.util.Timer; import java.util.TimerTask; import java.util.concurrent.TimeUnit; /** * @author :jiaolian * @date :Created in 2021-02-25 13:50 * @description:Timer任務異常,Timer執行緒退出! * @modified By: * 公眾號:叫練 */ public class TimerTaskExceptionTest { public static void main(String[] args) throws InterruptedException { Timer timer = new Timer(); timer.schedule(new TimerTask() { @Override public void run() { System.out.println("first_task"); } },0,1000); TimeUnit.SECONDS.sleep(3); timer.schedule(new TimerTask() { @Override public void run() { System.out.println("second_task"); int x = 5/0; } },0,1000); } }
如上程式碼:timer提交了first_task和second_task兩個任務,3秒後,second_task執行5/0會丟擲異常,此時timer執行緒會退出。如果換成ScheduledThreadPoolExecutor則不會影響first_task。在實際應用中,推薦用定時任務執行緒池中的方法去處理任務。
總結
今天我們介紹了執行緒池中面試中幾個重要的面試點,整理出來希望能對你有幫助,寫的比不全,同時還有許多需要修正的地方,希望親們加以指正和點評,喜歡的請點贊加關注哦。點關注,不迷路,我是叫練【公眾號】,微訊號【jiaolian123abc】邊叫邊練。