Java Timer类源码解析是怎样的?

2026-05-26 01:401阅读0评论SEO基础
  • 内容介绍
  • 文章标签
  • 相关推荐

本文共计1963个文字,预计阅读时间需要8分钟。

Java Timer类源码解析是怎样的?

目录

一、TimerTask

1.任务状态

2.任务属性说明

3.任务方法说明

Java Timer类源码解析是怎样的?

二、Timer

1.sched方法

2.cancel方法

3.purge方法

三、TaskQueue

四、TimerThread

五、通过源码分析,深入了解其底层原理

目录
  • 一、TimerTask
    • 1. 任务状态
    • 2. 任务属性说明
    • 3. 任务方法说明
  • 二、Timer
    • 1. sched方法
    • 2. cancel方法
    • 3. purge方法
  • 三、TaskQueue
    • 四、TimerThread

      通过源码分析,我们可以更深入的了解其底层原理。

      对于JDK自带的定时器,主要涉及TimerTask类、Timer类、TimerQueue类、TimerThread类,其中TimerQueue和TimerThread类与Timer类位于同一个类文件,由Timer内部调用。

      先画上一张图,描述一下Timer的大致模型,Timer的模型很容易理解,即任务加入到任务队列中,由任务处理线程循环从任务队列取出任务执行:

      一、TimerTask

      TimerTask是一个任务抽象类,实现了Runnable接口,是可被线程执行的。

      1. 任务状态

      在TimerTask中定义了关于任务状态的常量字段:

      // 未调度状态 static final int VIRGIN = 0; // 任务已调度,但未执行 static final int SCHEDULED = 1; // 若是一次性任务表示已执行;可重复执行任务,该状态无效 static final int EXECUTED = 2; // 任务被取消 static final int CANCELLED = 3;

      当一个TimerTask对象创建后,其初始状态为VIRGIN;

      当调用Timer的schedule方法调度了此TimerTask对象后,其状态变更为SCHEDULED;

      如果TimerTask是一次性任务,此任务执行后,状态将变为EXECUTED,可重复执行任务执行后状态不变;

      当中途调用了TimerTask.cancel方法,该任务的状态将变为CANCELLED。

      2. 任务属性说明

      TimerTask中,有如下成员变量:

      // 用于加锁控制多线程修改TimerTask内部状态 final Object lock = new Object(); // 任务状态,初始状态为待未调度状态 int state = VIRGIN; // 任务的下一次执行时间点 long nextExecutionTime; // 任务执行的时间间隔。正数表示固定速率;负数表示固定时延;0表示只执行一次 long period = 0;

      3. 任务方法说明

      TimerTask中有三个方法:

      • run:实现了Runnable接口,创建TimerTask需要重写此方法,编写任务执行代码
      • cancel:取消任务
      • scheduledExecutionTime:计算执行时间点

      3.1. Cancel方法

      cancel方法的实现代码:

      public boolean cancel() { synchronized(lock) { boolean result = (state == SCHEDULED); state = CANCELLED; return result; } }

      在cancel方法内,使用synchronized加锁,这是因为Timer内部的线程会对TimerTask状态进行修改,而调用cancel方法一般会是另外一个线程。

      为了避免线程同步问题,cancel在修改状态前进行了加锁操作。

      调用cancel方法将会把任务状态变更为CANCELLED状态,即任务取消状态,并返回一个布尔值,该布尔值表示此任务之前是否已是SCHEDULED 已调度状态。

      3.2. scheduledExecutionTime方法

      scheduledExecutionTime方法实现:

      public long scheduledExecutionTime() { synchronized(lock) { return (period < 0 ? nextExecutionTime + period : nextExecutionTime - period); } }

      该方法返回此任务的下次执行时间点。

      二、Timer

      分析Timer源代码,Timer在内部持有了两个成员变量:

      private final TaskQueue queue = new TaskQueue(); private final TimerThread thread = new TimerThread(queue);

      TaskQueue是任务队列,TimerThread是任务处理线程。

      1. sched方法

      无论是使用schedule还是scheduleAtFixedRate方法来调度任务,Timer内部最后都是调用sched方法进行处理。

      public void schedule(TimerTask task, Date time) { sched(task, time.getTime(), 0); // 一次性任务,period为0 } public void schedule(TimerTask task, long delay) { ... sched(task, System.currentTimeMillis()+delay, 0); // 一次性任务,period为0 } public void schedule(TimerTask task, long delay, long period) { ... sched(task, System.currentTimeMillis()+delay, -period); // 固定延时模式,-period } public void schedule(TimerTask task, Date firstTime, long period) { ... sched(task, firstTime.getTime(), -period); // 固定延时模式,-period } public void scheduleAtFixedRate(TimerTask task, long delay, long period) { ... sched(task, System.currentTimeMillis()+delay, period); // 固定速率模式,period为正 } public void scheduleAtFixedRate(TimerTask task, Date firstTime, long period) { ... sched(task, firstTime.getTime(), period); // 固定速率模式,period为正 }

      sched方法核心代码:

      private void sched(TimerTask task, long time, long period) { ... // 加锁,避免外部其他线程同时调用cancel,同时访问queue产生线程同步问题 synchronized(queue) { // 如果线程已终止,抛出异常 if (!thread.newTasksMayBeScheduled) throw new IllegalStateException("Timer already cancelled."); // 加锁,避免多线程访问同一个任务产生线程同步问题 synchronized(task.lock) { // task的状态必须为VIRGIN,否则认为已经加入调度或者已经取消了,避免重复的调度 if (task.state != TimerTask.VIRGIN) throw new IllegalStateException( "Task already scheduled or cancelled"); // 设置下次执行时间点 task.nextExecutionTime = time; // 设置时间间隔 task.period = period; // 任务状态变更为已调度 task.state = TimerTask.SCHEDULED; } // 将任务添加到队列中 queue.add(task); // 如果此任务是最近的任务,唤醒线程 if (queue.getMin() == task) queue.notify(); } }

      2. cancel方法

      cancel方法一般是由外部其他线程调用,而Timer内部的线程也会对任务队列进行操作,因此加锁。

      public void cancel() { synchronized(queue) { // 修改线程的循环执行标志,令线程能够终止 thread.newTasksMayBeScheduled = false; // 清空任务队列 queue.clear(); // 唤醒线程 queue.notify(); } }

      3. purge方法

      当通过TimerTask.cancel将任务取消后,Timer的任务队列还引用着此任务,Timer只有到了要执行时才会移除,其他时候并不会自动将此任务移除,需要调用purge方法进行清理。

      public int purge() { int result = 0; synchronized(queue) { // 遍历队列,将CANCELLED状态的任务从任务队列中移除 for (int i = queue.size(); i > 0; i--) { if (queue.get(i).state == TimerTask.CANCELLED) { queue.quickRemove(i); result++; } } // 如果移除任务数不为0,触发重新排序 if (result != 0) queue.heapify(); } // 返回移除任务数 return result; }

      三、TaskQueue

      TaskQueue是Timer类文件中封装的一个队列数据结构,内部默认是一个长度128的TimerTask数组,当任务加入时,检测到数组将满将会自动扩容1倍,并对数组元素根据下次执行时间nextExecutionTime按时间从近到远进行排序。

      void add(TimerTask task) { // 检测数组长度,若不够则进行扩容 if (size + 1 == queue.length) queue = Arrays.copyOf(queue, 2*queue.length); // 任务入队 queue[++size] = task; // 排序 fixUp(size); }

      fixUp方法实现:

      private void fixUp(int k) { while (k > 1) { int j = k >> 1; if (queue[j].nextExecutionTime <= queue[k].nextExecutionTime) break; TimerTask tmp = queue[j]; queue[j] = queue[k]; queue[k] = tmp; k = j; } }

      TaskQueue中除了fixUp方法外还有一个fixDown方法,这两个其实就是堆排序算法,在算法专题中再进行详细介绍,只要记住他们的任务就是按时间从近到远进行排序,最近的任务排在队首即可。

      private void fixDown(int k) { int j; while ((j = k << 1) <= size && j > 0) { if (j < size && queue[j].nextExecutionTime > queue[j+1].nextExecutionTime) j++; // j indexes smallest kid if (queue[k].nextExecutionTime <= queue[j].nextExecutionTime) break; TimerTask tmp = queue[j]; queue[j] = queue[k]; queue[k] = tmp; k = j; } } void heapify() { for (int i = size/2; i >= 1; i--) fixDown(i); }

      四、TimerThread

      TimerThread的核心代码位于mainLoop方法:

      private void mainLoop() { // 死循环,从队列取任务执行 while (true) { try { TimerTask task; boolean taskFired; // 对任务队列加锁 synchronized(queue) { // 如果队列中没有任务,则进入等待,newTasksMayBeScheduled是线程运行标志位,为false时将退出循环 while (queue.isEmpty() && newTasksMayBeScheduled) queue.wait(); // 如果任务队列是空的还执行到这一步,说明newTasksMayBeScheduled为false,退出循环 if (queue.isEmpty()) break; long currentTime, executionTime; // 从队列取得最近的任务 task = queue.getMin(); // 加锁 synchronized(task.lock) { // 如果任务状态是已取消,则移除该任务,重新循环取任务 if (task.state == TimerTask.CANCELLED) { queue.removeMin(); continue; } // 当前时间 currentTime = System.currentTimeMillis(); // 任务的执行时间点 executionTime = task.nextExecutionTime; // 如果执行时间点早于或等于当前时间,即过期/时间到了,则触发任务执行 if (taskFired = (executionTime<=currentTime)) { // 如果任务period=0,即一次性任务 if (task.period == 0) { // 从队列移除一次性任务 queue.removeMin(); // 任务状态变更为已执行 task.state = TimerTask.EXECUTED; } else { // 可重复执行任务,重新进行调度,period<0是固定时延,period>0是固定速率 queue.rescheduleMin( task.period<0 ? currentTime - task.period // 计算下次执行时间 : executionTime + task.period); } } } // taskFired为false即任务尚未到执行时间点,进行等待,等待时间是 执行时间点 - 当前时间点 if (!taskFired) queue.wait(executionTime - currentTime); } // taskFired为true表示已触发,执行任务 if (taskFired) task.run(); } catch(InterruptedException e) { } } }

      以上就是Java定时器Timer的源码分析的详细内容,更多关于Java Timer的资料请关注自由互联其它相关文章!

      本文共计1963个文字,预计阅读时间需要8分钟。

      Java Timer类源码解析是怎样的?

      目录

      一、TimerTask

      1.任务状态

      2.任务属性说明

      3.任务方法说明

      Java Timer类源码解析是怎样的?

      二、Timer

      1.sched方法

      2.cancel方法

      3.purge方法

      三、TaskQueue

      四、TimerThread

      五、通过源码分析,深入了解其底层原理

      目录
      • 一、TimerTask
        • 1. 任务状态
        • 2. 任务属性说明
        • 3. 任务方法说明
      • 二、Timer
        • 1. sched方法
        • 2. cancel方法
        • 3. purge方法
      • 三、TaskQueue
        • 四、TimerThread

          通过源码分析,我们可以更深入的了解其底层原理。

          对于JDK自带的定时器,主要涉及TimerTask类、Timer类、TimerQueue类、TimerThread类,其中TimerQueue和TimerThread类与Timer类位于同一个类文件,由Timer内部调用。

          先画上一张图,描述一下Timer的大致模型,Timer的模型很容易理解,即任务加入到任务队列中,由任务处理线程循环从任务队列取出任务执行:

          一、TimerTask

          TimerTask是一个任务抽象类,实现了Runnable接口,是可被线程执行的。

          1. 任务状态

          在TimerTask中定义了关于任务状态的常量字段:

          // 未调度状态 static final int VIRGIN = 0; // 任务已调度,但未执行 static final int SCHEDULED = 1; // 若是一次性任务表示已执行;可重复执行任务,该状态无效 static final int EXECUTED = 2; // 任务被取消 static final int CANCELLED = 3;

          当一个TimerTask对象创建后,其初始状态为VIRGIN;

          当调用Timer的schedule方法调度了此TimerTask对象后,其状态变更为SCHEDULED;

          如果TimerTask是一次性任务,此任务执行后,状态将变为EXECUTED,可重复执行任务执行后状态不变;

          当中途调用了TimerTask.cancel方法,该任务的状态将变为CANCELLED。

          2. 任务属性说明

          TimerTask中,有如下成员变量:

          // 用于加锁控制多线程修改TimerTask内部状态 final Object lock = new Object(); // 任务状态,初始状态为待未调度状态 int state = VIRGIN; // 任务的下一次执行时间点 long nextExecutionTime; // 任务执行的时间间隔。正数表示固定速率;负数表示固定时延;0表示只执行一次 long period = 0;

          3. 任务方法说明

          TimerTask中有三个方法:

          • run:实现了Runnable接口,创建TimerTask需要重写此方法,编写任务执行代码
          • cancel:取消任务
          • scheduledExecutionTime:计算执行时间点

          3.1. Cancel方法

          cancel方法的实现代码:

          public boolean cancel() { synchronized(lock) { boolean result = (state == SCHEDULED); state = CANCELLED; return result; } }

          在cancel方法内,使用synchronized加锁,这是因为Timer内部的线程会对TimerTask状态进行修改,而调用cancel方法一般会是另外一个线程。

          为了避免线程同步问题,cancel在修改状态前进行了加锁操作。

          调用cancel方法将会把任务状态变更为CANCELLED状态,即任务取消状态,并返回一个布尔值,该布尔值表示此任务之前是否已是SCHEDULED 已调度状态。

          3.2. scheduledExecutionTime方法

          scheduledExecutionTime方法实现:

          public long scheduledExecutionTime() { synchronized(lock) { return (period < 0 ? nextExecutionTime + period : nextExecutionTime - period); } }

          该方法返回此任务的下次执行时间点。

          二、Timer

          分析Timer源代码,Timer在内部持有了两个成员变量:

          private final TaskQueue queue = new TaskQueue(); private final TimerThread thread = new TimerThread(queue);

          TaskQueue是任务队列,TimerThread是任务处理线程。

          1. sched方法

          无论是使用schedule还是scheduleAtFixedRate方法来调度任务,Timer内部最后都是调用sched方法进行处理。

          public void schedule(TimerTask task, Date time) { sched(task, time.getTime(), 0); // 一次性任务,period为0 } public void schedule(TimerTask task, long delay) { ... sched(task, System.currentTimeMillis()+delay, 0); // 一次性任务,period为0 } public void schedule(TimerTask task, long delay, long period) { ... sched(task, System.currentTimeMillis()+delay, -period); // 固定延时模式,-period } public void schedule(TimerTask task, Date firstTime, long period) { ... sched(task, firstTime.getTime(), -period); // 固定延时模式,-period } public void scheduleAtFixedRate(TimerTask task, long delay, long period) { ... sched(task, System.currentTimeMillis()+delay, period); // 固定速率模式,period为正 } public void scheduleAtFixedRate(TimerTask task, Date firstTime, long period) { ... sched(task, firstTime.getTime(), period); // 固定速率模式,period为正 }

          sched方法核心代码:

          private void sched(TimerTask task, long time, long period) { ... // 加锁,避免外部其他线程同时调用cancel,同时访问queue产生线程同步问题 synchronized(queue) { // 如果线程已终止,抛出异常 if (!thread.newTasksMayBeScheduled) throw new IllegalStateException("Timer already cancelled."); // 加锁,避免多线程访问同一个任务产生线程同步问题 synchronized(task.lock) { // task的状态必须为VIRGIN,否则认为已经加入调度或者已经取消了,避免重复的调度 if (task.state != TimerTask.VIRGIN) throw new IllegalStateException( "Task already scheduled or cancelled"); // 设置下次执行时间点 task.nextExecutionTime = time; // 设置时间间隔 task.period = period; // 任务状态变更为已调度 task.state = TimerTask.SCHEDULED; } // 将任务添加到队列中 queue.add(task); // 如果此任务是最近的任务,唤醒线程 if (queue.getMin() == task) queue.notify(); } }

          2. cancel方法

          cancel方法一般是由外部其他线程调用,而Timer内部的线程也会对任务队列进行操作,因此加锁。

          public void cancel() { synchronized(queue) { // 修改线程的循环执行标志,令线程能够终止 thread.newTasksMayBeScheduled = false; // 清空任务队列 queue.clear(); // 唤醒线程 queue.notify(); } }

          3. purge方法

          当通过TimerTask.cancel将任务取消后,Timer的任务队列还引用着此任务,Timer只有到了要执行时才会移除,其他时候并不会自动将此任务移除,需要调用purge方法进行清理。

          public int purge() { int result = 0; synchronized(queue) { // 遍历队列,将CANCELLED状态的任务从任务队列中移除 for (int i = queue.size(); i > 0; i--) { if (queue.get(i).state == TimerTask.CANCELLED) { queue.quickRemove(i); result++; } } // 如果移除任务数不为0,触发重新排序 if (result != 0) queue.heapify(); } // 返回移除任务数 return result; }

          三、TaskQueue

          TaskQueue是Timer类文件中封装的一个队列数据结构,内部默认是一个长度128的TimerTask数组,当任务加入时,检测到数组将满将会自动扩容1倍,并对数组元素根据下次执行时间nextExecutionTime按时间从近到远进行排序。

          void add(TimerTask task) { // 检测数组长度,若不够则进行扩容 if (size + 1 == queue.length) queue = Arrays.copyOf(queue, 2*queue.length); // 任务入队 queue[++size] = task; // 排序 fixUp(size); }

          fixUp方法实现:

          private void fixUp(int k) { while (k > 1) { int j = k >> 1; if (queue[j].nextExecutionTime <= queue[k].nextExecutionTime) break; TimerTask tmp = queue[j]; queue[j] = queue[k]; queue[k] = tmp; k = j; } }

          TaskQueue中除了fixUp方法外还有一个fixDown方法,这两个其实就是堆排序算法,在算法专题中再进行详细介绍,只要记住他们的任务就是按时间从近到远进行排序,最近的任务排在队首即可。

          private void fixDown(int k) { int j; while ((j = k << 1) <= size && j > 0) { if (j < size && queue[j].nextExecutionTime > queue[j+1].nextExecutionTime) j++; // j indexes smallest kid if (queue[k].nextExecutionTime <= queue[j].nextExecutionTime) break; TimerTask tmp = queue[j]; queue[j] = queue[k]; queue[k] = tmp; k = j; } } void heapify() { for (int i = size/2; i >= 1; i--) fixDown(i); }

          四、TimerThread

          TimerThread的核心代码位于mainLoop方法:

          private void mainLoop() { // 死循环,从队列取任务执行 while (true) { try { TimerTask task; boolean taskFired; // 对任务队列加锁 synchronized(queue) { // 如果队列中没有任务,则进入等待,newTasksMayBeScheduled是线程运行标志位,为false时将退出循环 while (queue.isEmpty() && newTasksMayBeScheduled) queue.wait(); // 如果任务队列是空的还执行到这一步,说明newTasksMayBeScheduled为false,退出循环 if (queue.isEmpty()) break; long currentTime, executionTime; // 从队列取得最近的任务 task = queue.getMin(); // 加锁 synchronized(task.lock) { // 如果任务状态是已取消,则移除该任务,重新循环取任务 if (task.state == TimerTask.CANCELLED) { queue.removeMin(); continue; } // 当前时间 currentTime = System.currentTimeMillis(); // 任务的执行时间点 executionTime = task.nextExecutionTime; // 如果执行时间点早于或等于当前时间,即过期/时间到了,则触发任务执行 if (taskFired = (executionTime<=currentTime)) { // 如果任务period=0,即一次性任务 if (task.period == 0) { // 从队列移除一次性任务 queue.removeMin(); // 任务状态变更为已执行 task.state = TimerTask.EXECUTED; } else { // 可重复执行任务,重新进行调度,period<0是固定时延,period>0是固定速率 queue.rescheduleMin( task.period<0 ? currentTime - task.period // 计算下次执行时间 : executionTime + task.period); } } } // taskFired为false即任务尚未到执行时间点,进行等待,等待时间是 执行时间点 - 当前时间点 if (!taskFired) queue.wait(executionTime - currentTime); } // taskFired为true表示已触发,执行任务 if (taskFired) task.run(); } catch(InterruptedException e) { } } }

          以上就是Java定时器Timer的源码分析的详细内容,更多关于Java Timer的资料请关注自由互联其它相关文章!