pring Scheduler定時器原理分析
首先我們看看簡單定時器實現方法:用ScheduledExecutorService介面
public interface ScheduledExecutorService extends ExecutorService { 建立並執行在給定延遲後啟用的一次性操作。 ScheduledFuture<?> schedule(Runnable command,long delay,TimeUnit unit); 建立並執行在給定延遲後啟用的 ScheduledFuture。<V> ScheduledFuture<V> schedule(Callable<V> callable,long delay,TimeUnit unit); 建立並執行一個在給定初始延遲後首次啟用的定期操作,後續操作具有給定的週期;也就是將在 initialDelay 後開始執行,然後在 initialDelay+period 後執行,接著在 initialDelay + 2 * period 後執行,依此類推。如果任務的任何一個執行遇到異常,則後續執行都會被取消。否則,只能通過執行程式的取消或終止方法來終止該任務。如果此任務的任何一個執行要花費比其週期更長的時間,則將推遲後續執行,但不會同時執行。 ScheduledFuture<?> scheduleAtFixedRate(Runnable command,long initialDelay,long period, TimeUnit unit); 建立並執行一個在給定初始延遲後首次啟用的定期操作,隨後,在每一次執行終止和下一次執行開始之間都存在給定的延遲。如果任務的任一執行遇到異常,就會取消後續執行。否則,只能通過執行程式的取消或終止方法來終止該任務。 ScheduledFuture<?> scheduleWithFixedDelay(Runnable command,long initialDelay,long delay,TimeUnit unit); }
用法示例
以下是一個帶方法的類,它設定了 ScheduledExecutorService ,在 1 小時內每 10 秒鐘蜂鳴一次:
import static java.util.concurrent.TimeUnit.*; class BeeperControl { private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1); public void beepForAnHour() { final Runnable beeper = new Runnable() { public void run() { System.out.println("beep"); } }; final ScheduledFuture<?> beeperHandle = scheduler.scheduleAtFixedRate(beeper, 10, 10, SECONDS); scheduler.schedule(new Runnable() { public void run() { beeperHandle.cancel(true); } }, 60 * 60, SECONDS); } }
裡面幾個方法其中說到的當業務中出現異常後,就不會執行後續定時任務了,所以如果要用ScheduledExecutorService 寫一些自己的業務定時任務,務必要知道這點。
再來說說Spring Scheduler。
Spring3.0之後增加了排程器功能, 提供的@Schedule等等註解, 那麼它內部是如何實現的呢?
核心類摘要:
1.ScheduledAnnotationBeanPostProcessor
2.ScheduledTaskRegistrar
3.TaskScheduler
4.ReschedulingRunnable
具體說明:
1.ScheduledAnnotationBeanPostProcessor
(1)核心方法:Object postProcessAfterInitialization(final Object bean, String beanName)
功能:負責@Schedule註解的掃描,構建ScheduleTask
(2)核心方法:onApplicationEvent(ContextRefreshedEvent event)
功能:spring容器載入完畢之後呼叫,ScheduleTask向ScheduledTaskRegistrar中註冊, 呼叫ScheduledTaskRegistrar.afterPropertiesSet()
2.ScheduledTaskRegistrar
(1)核心方法:void afterPropertiesSet()
功能:初始化所有定時器,啟動定時器
3.TaskScheduler
主要的實現類有三個 ThreadPoolTaskScheduler, ConcurrentTaskScheduler,TimerManagerTaskScheduler
作用:這些類的作用主要是將task和executor用ReschedulingRunnable包裝起來進行生命週期管理。
(1)核心方法:ScheduledFuture schedule(Runnable task, Trigger trigger)
4.ReschedulingRunnable
(1)核心方法:public ScheduledFuture schedule()
(2)核心方法:public void run()
public ScheduledFuture schedule() { synchronized (this.triggerContextMonitor) { this.scheduledExecutionTime = this.trigger.nextExecutionTime(this.triggerContext); if (this.scheduledExecutionTime == null) { return null; } long initialDelay = this.scheduledExecutionTime.getTime() - System.currentTimeMillis(); this.currentFuture = this.executor.schedule(this, initialDelay, TimeUnit.MILLISECONDS); return this; } }Java程式碼
@Override public void run() { Date actualExecutionTime = new Date(); super.run(); Date completionTime = new Date(); synchronized (this.triggerContextMonitor) { this.triggerContext.update(this.scheduledExecutionTime, actualExecutionTime, completionTime); } if (!this.currentFuture.isCancelled()) { schedule(); } }
通過schedule方法及run方法互相呼叫,再利用ScheduledExecutorService介面的schedule(Runnable command,long delay,TimeUnit unit)單次執行效果,從而實現一定時間重複觸發的效果。
以上說的都是以@Scheduled(cron = "0 0 2,13,16 ? * *")和預設配置下執行的,呼叫的TaskScheduler的ConcurrentTaskScheduler實現類,預設單個執行緒執行。
要以執行緒池執行的話,需要配置:
<task:annotation-driven scheduler="myScheduler"/>
<task:scheduler id="myScheduler" pool-size="20"/>
給出幾個結論:
排程器本質上還是通過juc的ScheduledExecutorService進行的
排程器啟動後你無法通過修改系統時間達到讓它馬上執行的效果
被@Schedule註解的方法如果有任何Throwable出現, 不會中斷後續Task, 預設只會列印Error日誌,定時任務不會同時被觸發。