定时器Timer类是一个最基础的定时调度任务的类。在日常开发中经常会遇到。

  • 这个类的参数有以下几个:
    (1)定时器任务队列,上面的定时器线程就是从这个队列中取任务来执行。有新任务需要加入也是放到这个队列中。

private final TaskQueue queue = new TaskQueue();

        (2)定时器线程,负责调度定时器任务。

private final TimerThread thread = new TimerThread(queue);
  • 默认的构造函数有以下4个:
    (1)无参数构造函数

 Timer() {       
(  serialNumber());
}

        这个构造函数调用了另一个构造函数,从而给线程设置一个默认的名字。这个名字就是Timer-加上一个serialNumber(); 我们来看看这个serialNumber()是什么鬼?

    nextSerialNumber ();
   serialNumber() {     
 nextSerialNumbergetAndIncrement();
}

      原来这个serialNumber就是一个自增的数据,这个调用了AtomicInteger类来实现技术的。 这个设置为静态,说明我们在用户代码中假如创建多个定时器,也能保证每个定时器能得到一个独一无二的、递增的序列号。

(2)这个构造函数可以用户自定义线程名称。

public Timer(String name) {
      thread.setName(name);
      thread.start();
}

(3)这个构造函数使用默认的名字和设置守护线。

 Timer( isDaemon) {        
(  serialNumber(), isDaemon);
}

(4)这个构造函数可以设置名字和设置守护线程.

 Timer( name,  isDaemon) {
      threadsetName(name);
      threadsetDaemon(isDaemon);
      threadstart();
}

小结:创建了定时器对象,在构造函数中会帮我们启动线程的。接下来就是我们调用schedule函数来调度任务。

接着,我们来看一下schedule这个方法,有哪些方法和参数。
(1)这个方法就是提交一个任务,并告诉线程多久以后执行。只执行一次任务。

  schedule( task,  delay) {	
 (delay  )	    
  ();
  sched(task, currentTimeMillis()delay, );
}

(2)这个方法提交一个任务,并指定什么日期执行,只执行一次任务。

public void schedule(TimerTask task, Date time) {
        sched(task, time.getTime(), 0);
}

(3)这个方法提交一个任务,并指定延长delay时间执行,周期是period。

  schedule( task,  delay,  period) {	
 (delay  )            
  ();	
 (period  )	    
  ();
    sched(task, currentTimeMillis()delay, period);
}

(4)这个方法和上面的类似,只是传递的是日期。

  schedule( task, firstTime, period){
(period  )	    
  ();
  sched(task, firstTimegetTime(), period);
}

        同样的,Timer类还提供了两个scheduleAtFixedRate方法。 区别在于,schedule 下一次的执行时间点=上一次程序执行完成的时间点+间隔时间. 而scheduleAtFixedRate 下一次的执行时间点=上一次程序执行完成的开始时间点+间隔时间. 我们看scheduleAtFixedRate的方法可以看出,传递第三个参数有正反分。

  scheduleAtFixedRate( task, delay, period)
{        
 (delay  )            
  ();        
 (period  )            
  ();
 sched(task, currentTimeMillis()delay, period);
}


小结:Timer类提供了定时调度任务的schedule和scheduleAtFixedRate方法,有点区别。区别在于一个以上一次任务完成时间作为起点,一个以起始时间作为起点。

     接下来我们看看这个TimerTask类,看看这个类有啥特殊的?这个TimerThread是一个抽象的类,这个类继承了Thread类。其中抽象方法为:

public abstract void run();

      我们看看这个类中还有其他什么?通过下面几个变量标识线程的不同工作状态,默认值为0。

 state  ;	
     ;	
       ;	
        ;	
       ;

      这个类中还有两个和调度相关的变量:

 long nextExecutionTime;//下一次任务执行时间 

long period = 0; //任务调度周期,也就是每多长时间调度一次 我们看看这个类中还有个取消的方法,这个方法只是将线程状态标识为cannel状态。

  cancel() {		
(lock) {		 
 result  (state  );
	state  ;		 
 result;
  }
}

下面是定时器调度算法的核心:

定时器Timer源码解析_java

接着,我们来看看TimerThread中的run方法。

  run() {	    
 {
	mainLoop();
   }  {	(queue) {
	    newTasksMayBeScheduled  ;
	    queueclear(); 
           }
  }
}


这里,我们重点分析mainLoop方法,这个是定时器调度任务的基础。

定时器Timer源码解析_java_02

小结:在我们创建Timer对象的时候,其实就启动了TimerThread线程。在我们调用schedule或者scheduleAtFixedRate时候, 我们将我们的任务(也就是Runnable对象)包装成TimerTask对象,以及延迟参数和周期参数传递进task队列中。 这样TimerThread在执行任务的时候拿到这个任务的这些参数,从而判断是一次性任务还是周期性任务(通过period是否为0), 然后继续将任务添加到Queue中。每次去Queue中去任务的时候,都是取最小的任务,也就是离当前时间最近的任务进行处理。