前言
先來看一段面試中的場景
面試官: 我看你已經有一定工作年限了,那請問有沒有研究得比較深的一些技術棧呢?
你: 目前沒有太深的研究,暫時還主要是處於會使用的階段,我用過xxx,能幹活肯定是沒問題的!
面試官: 那定時器在專案中有用過嗎,平時都是怎麼使用的?
你: 當然有,我們是基於SpringBoot做的定時器,使用很簡單,就是打上註解就行.
面試官: 既然你有使用過,那我們就聊一下SpringBoot定時器使用層面的問題,不問深的,甚至分散式叢集也暫時考慮,就只考慮最簡單的**
單執行緒模型
**,那請問假如我定時任務是5秒執行一次,但是第一個任務就執行了8秒,第二個會怎麼樣呢?你詳細說一下各種情況.
你: 這個...這個真沒有考慮過
面試官: 我都還沒問原理,我這只是問最最基本的使用問題,要不今天先到這裡,你回去等通知吧.
定時器的使用分析
SpingBoot的定時器我相信大家都會使用,即使不會使用的,隨便搜尋一下,相信都能在30分鐘內上手.但是其實Spring的定時器有三種模式,分別是fixedDelay
、cron
、fixedRate
.那這三種分別有什麼區別呢?我們來分別寫一個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
複製程式碼
寫在最後
肥朝 是一個專注於 原理、原始碼、開發技巧的技術公眾號,號內原創專題式原始碼解析、真實場景原始碼原理實戰(重點)。掃描下面二維碼關注肥朝,讓本該造火箭的你,不再擰螺絲!