圖解定時任務線程池


線程池概念


我們上篇文章分析了ThreadPoolExecutor,如果要用一句話說明它的主要優勢,就是線程置換。還有Executors工具類,極大的簡化了研發人員工作。

我用一個圖重復描述下線程池概念。多生產-多消費模型。

image.png

 

  • 生產者將線程任務丟進線程池中,生產者就就結束了。
  • 線程池控制消費者消費元素,消費者可以是1個或者多個,取決於線程池參數corePoolSize和maxPoolSize設置。
  • 阻塞隊列是用來裝生產者丟進去的線程任務,如ArrayBlockingQueue,LinkedBlockingQueue,DelayedQueue等。如果生產者生產能力超過消費者消費能力,如果阻塞隊列有長度限制並且超過隊列長度線程池會執行飽和策略,如果隊列沒有長度限制,可也能出現OOM哦,因為線程任務可能把內存都撐爆了,這也是面試常考點哦!

詳細概念可以翻看我上一篇文章《線程池面試必考問題》。

 

 

定時任務延時原理


還記得我們上面說的阻塞隊列嗎?定時任務線程池底層使用DelayedQueue實現的,這種延遲隊列有一個最大的特點:按時出隊列,大家都考過駕照吧,科目三考試的時候都是車上坐的是4個人,假設一個人考試需要花15分鍾,那么考試學員隊列看起來是這樣的。

image.png

DelayedQueue底層需要實現Delayed接口同時需要實現getDelay方法和compareTo方法,getDelay方法用於計算出隊列時間,一旦小於0就會出隊列;compareTo方法用於按觸發時間從小到大排序。這就是Schedule線程池任務延時原理,如果需要看案例代碼,請參考我文章《並發隊列:PriorityBlockingQueue和DelayQueue案例使用》。

 

scheduleWithFixedDelay和scheduleAtFixedRate區別


image.png

由上圖可知:假設線程任務:耗時1秒,定時3秒執行,scheduleWithFixedDelay其實是4秒執行一次。

  • scheduleWithFixedDelay:是以任務結束時間周期運行。
  • scheduleAtFixedRate:是以固定周期運行。

 

 

FutureTask獲取返回值


在ScheduledThreadPoolExecutor中,ScheduledFutureTask是獲取定時任務返回值,繼承FutureTask。我們看下FutureTask調用get阻塞簡化流程圖。

image.png

  1. 向線程池添加任務,任務被封裝成ScheduledFutureTask並且實現Callable接口是為了獲取任務返回值。
  2. 當客戶端線程通過get方式獲取線程池線程執行的返回值,客戶端線程會阻塞直到線程池線程任務執行完。

在實際運用,我們一般拿返回值測試多線程性能。

 

 

Timer比較


  1. Timer是單線程,而且不帶返回值。ScheduledThreadPoolExecutor是多線程的,采用線程復用代替創建新的線程,並且FutureTask帶返回值。
  2. 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】邊叫邊練。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM