1. 程式人生 > >pring Scheduler定時器原理分析

pring Scheduler定時器原理分析

首先我們看看簡單定時器實現方法:用ScheduledExecutorService介面 
  

Java程式碼  
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 秒鐘蜂鳴一次: 
  

Java程式碼  
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() 

Java程式碼  
 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日誌,定時任務不會同時被觸發。