線程池概念
我們上篇文章分析了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】邊叫邊練。