[肥朝]原理暫且不談,定時器你當真會用?

肥朝發表於2019-04-12

前言

先來看一段面試中的場景

面試官: 我看你已經有一定工作年限了,那請問有沒有研究得比較深的一些技術棧呢?

你: 目前沒有太深的研究,暫時還主要是處於會使用的階段,我用過xxx,能幹活肯定是沒問題的!

面試官: 那定時器在專案中有用過嗎,平時都是怎麼使用的?

你: 當然有,我們是基於SpringBoot做的定時器,使用很簡單,就是打上註解就行.

面試官: 既然你有使用過,那我們就聊一下SpringBoot定時器使用層面的問題,不問深的,甚至分散式叢集也暫時考慮,就只考慮最簡單的**單執行緒模型**,那請問假如我定時任務是5秒執行一次,但是第一個任務就執行了8秒,第二個會怎麼樣呢?你詳細說一下各種情況.

你: 這個...這個真沒有考慮過

面試官: 我都還沒問原理,我這只是問最最基本的使用問題,要不今天先到這裡,你回去等通知吧.

[肥朝]原理暫且不談,定時器你當真會用?

定時器的使用分析

SpingBoot的定時器我相信大家都會使用,即使不會使用的,隨便搜尋一下,相信都能在30分鐘內上手.但是其實Spring的定時器有三種模式,分別是fixedDelaycronfixedRate.那這三種分別有什麼區別呢?我們來分別寫一個SpringBoot的定時器Demo來體驗一下.

fixedDelay方式

@Component
public class ScheduleHandle {

    private final Logger log = LoggerFactory.getLogger(ScheduleHandle.class);

    private List<Integer> index = Arrays.asList(8 * 1000, 3 * 1000, 6 * 1000, 2 * 1000, 2 * 1000);

    private AtomicInteger atomicInteger = new AtomicInteger(0);

    @Scheduled(fixedDelay = 3 * 1000)
    public void fixedDelay() throws Exception {
        int i = atomicInteger.get();
        if (i < 5) {
            Integer sleepTime = index.get(i);
            log.info("第{}個任務開始執行,執行時間為{}ms", i, sleepTime);
            Thread.sleep(sleepTime);
            atomicInteger.getAndIncrement();
        }
    }
}
複製程式碼

該方式最簡單,在上一個任務執行完成之後,間隔3秒(因為@Scheduled(fixedDelay = 3 * 1000))後,執行下一個任務.這種是最容易理解的,所以放在第一個來講.用一個圖來表示的話,更容易理解.如下:

[肥朝]原理暫且不談,定時器你當真會用?

可以用輸出日誌驗證上述結論

2019-04-07 21:59:11.761  INFO 29372 --- [pool-1-thread-1] com.toby.demo.job.ScheduleHandle         : 第0個任務開始執行,執行時間為8000ms
2019-04-07 21:59:22.772  INFO 29372 --- [pool-1-thread-1] com.toby.demo.job.ScheduleHandle         : 第1個任務開始執行,執行時間為3000ms
2019-04-07 21:59:28.777  INFO 29372 --- [pool-1-thread-1] com.toby.demo.job.ScheduleHandle         : 第2個任務開始執行,執行時間為6000ms
2019-04-07 21:59:37.783  INFO 29372 --- [pool-1-thread-1] com.toby.demo.job.ScheduleHandle         : 第3個任務開始執行,執行時間為2000ms
2019-04-07 21:59:42.785  INFO 29372 --- [pool-1-thread-1] com.toby.demo.job.ScheduleHandle         : 第4個任務開始執行,執行時間為2000ms
複製程式碼

cron

@Scheduled(cron = "0/5 * * * * ? ")
public void cron() throws Exception {
    int i = atomicInteger.get();
    if (i < 5) {
        Integer sleepTime = index.get(i);
        log.info("第{}個任務開始執行,執行時間為{}ms", i, sleepTime);
        Thread.sleep(sleepTime);
        atomicInteger.getAndIncrement();
    }
}
複製程式碼

因為上方程式碼配置如下:

//每5s執行一次
@Scheduled(cron = "0/5 * * * * ? ")
複製程式碼

因此你可以理解為5s就是一個週期.這就相當於在宿舍洗澡,因為只有一個洗澡位置(單執行緒),所以每次只能進去一個人,然後舍長在門口,每5s看一下有沒有空位,有空位的話叫下一個進去洗澡.

  • 第5秒的時候,舍長看了一下,發現第一個同學還沒有出來.

  • 第二個週期,也就是第10秒的時候再看一下.發現已經有空位了,那麼就叫第二個同學進去洗.

  • 第三個週期,也就是15秒的時候,又瞄了一眼,發現有空位了,叫第三個同學進去洗.

  • 第四個週期,也就是20秒的時候,發現沒有空位.

  • 第五個週期的時候,也就是25秒的時候.發現有空位了,接著叫下一個進去洗.剩下的不再分析.

用一張圖來表示如下:

[肥朝]原理暫且不談,定時器你當真會用?

可以用輸出日誌驗證上述結論

2019-04-07 22:15:30.002  INFO 29385 --- [pool-1-thread-1] com.toby.demo.job.ScheduleHandle         : 第0個任務開始執行,執行時間為8000ms
2019-04-07 22:15:40.001  INFO 29385 --- [pool-1-thread-1] com.toby.demo.job.ScheduleHandle         : 第1個任務開始執行,執行時間為3000ms
2019-04-07 22:15:45.001  INFO 29385 --- [pool-1-thread-1] com.toby.demo.job.ScheduleHandle         : 第2個任務開始執行,執行時間為6000ms
2019-04-07 22:15:55.001  INFO 29385 --- [pool-1-thread-1] com.toby.demo.job.ScheduleHandle         : 第3個任務開始執行,執行時間為2000ms
2019-04-07 22:16:00.001  INFO 29385 --- [pool-1-thread-1] com.toby.demo.job.ScheduleHandle         : 第4個任務開始執行,執行時間為2000ms
複製程式碼

fixedRate

@Scheduled(fixedRate = 5 * 1000)
public void fixedRate() throws Exception {
    int i = atomicInteger.get();
    if (i < 5) {
        Integer sleepTime = index.get(i);
        log.info("第{}個任務開始執行,執行時間為{}ms", i, sleepTime);
        Thread.sleep(sleepTime);
        atomicInteger.getAndIncrement();
    }
}
複製程式碼

還是要拿洗澡的這個例子來說.但是該方式和cron的方式有很大不同

因上方程式碼配置如下:

@Scheduled(fixedRate = 5 * 1000)
複製程式碼

你可以理解為舍長預算每個同學洗澡的時間是5秒,但是第一個同學進去洗澡,用了8秒.

  • 第二個同學本來應該在第5秒的時候就進去的,結果第一個同學出來的時候,已經是第8秒了,那麼舍長就趕緊催第二個同學進去,把時間進度追回來.

  • 第二個同學知道時間緊,洗了3秒就出來.此時舍長髮現,第三個同學,原本應該是在第10秒進去的,結果現在已經到了第11秒(8+3),那麼就趕緊催第三個同學進去.

  • 第三個同學沉醉其中,難以自拔的洗了6秒.出來的時候已經是第17秒(8+3+6).舍長掐指一算,發現第四個同學原本應該是第15秒的時候就進去了.結果現在都17秒了,時間不等人,催促第四個同學進去趕緊洗.

  • 第四個同學只洗了2秒就出來了,出來的時候,舍長看了一下時間,是第19秒."有原則"的舍長髮現,第5個同學原本預算是20秒的時候進去的,結果現在才19秒,不行,那讓他在外面玩1秒的手機,等20秒的時候再進去.

用一張圖來表示如下:

[肥朝]原理暫且不談,定時器你當真會用?

可以用輸出日誌驗證上述結論

2019-04-07 22:18:44.814  INFO 29390 --- [pool-1-thread-1] com.toby.demo.job.ScheduleHandle         : 第0個任務開始執行,執行時間為8000ms
2019-04-07 22:18:52.819  INFO 29390 --- [pool-1-thread-1] com.toby.demo.job.ScheduleHandle         : 第1個任務開始執行,執行時間為3000ms
2019-04-07 22:18:55.824  INFO 29390 --- [pool-1-thread-1] com.toby.demo.job.ScheduleHandle         : 第2個任務開始執行,執行時間為6000ms
2019-04-07 22:19:01.829  INFO 29390 --- [pool-1-thread-1] com.toby.demo.job.ScheduleHandle         : 第3個任務開始執行,執行時間為2000ms
2019-04-07 22:19:04.816  INFO 29390 --- [pool-1-thread-1] com.toby.demo.job.ScheduleHandle         : 第4個任務開始執行,執行時間為2000ms
複製程式碼

寫在最後

肥朝 是一個專注於 原理、原始碼、開發技巧的技術公眾號,號內原創專題式原始碼解析、真實場景原始碼原理實戰(重點)。掃描下面二維碼關注肥朝,讓本該造火箭的你,不再擰螺絲!

[肥朝]原理暫且不談,定時器你當真會用?

相關文章