广告
返回顶部
首页 > 资讯 > 后端开发 > Python >Java定时器Timer的源码分析
  • 116
分享到

Java定时器Timer的源码分析

Java Timer源码Java Timer定时器Java Timer 2022-11-13 19:11:58 116人浏览 安东尼

Python 官方文档:入门教程 => 点击学习

摘要

目录一、TimerTask1. 任务状态2. 任务属性说明3. 任务方法说明二、Timer1. sched方法2. cancel方法3. purge方法三、TaskQueue四、Ti

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

对于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的资料请关注编程网其它相关文章!

--结束END--

本文标题: Java定时器Timer的源码分析

本文链接: https://www.lsjlt.com/news/170596.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • Java定时器Timer的源码分析
    目录一、TimerTask1. 任务状态2. 任务属性说明3. 任务方法说明二、Timer1. sched方法2. cancel方法3. purge方法三、TaskQueue四、Ti...
    99+
    2022-11-13
    Java Timer源码 Java Timer定时器 Java Timer
  • Java多线程定时器Timer原理的示例分析
    这篇文章给大家分享的是有关Java多线程定时器Timer原理的示例分析的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。Timer的schedule(TimeTask task, Date time)的使用该方法的作...
    99+
    2023-05-30
    java
  • GoLang中的timer定时器实现原理分析
    // NewTimer creates a new Timer that will send // the current time on its channel after at ...
    99+
    2023-02-02
    Go timer定时器 Go timer Go定时器
  • Java中的定时器Timer详解
    目录总结简单来说,定时器就相当于一个“闹钟”,给定时器设定一个任务,约定这个任务在xxx时间之后执行~ Timer类提供了一个核心接口,schedule(安排) 指定一个任...
    99+
    2022-11-12
  • java定时器timer的使用方法代码示例
     1.首先肯定是容器一启动就要启动定时器,所以我们可以选择把定时器写在一个监听器里,容器一启动所以监听器也就跟着启动,然后定时器就可以工作了。第一步,把自己写的监听器加到web.xml中;第二步,写一个监听器,实现ServletC...
    99+
    2023-05-30
    java timer 定时器
  • Java多线程之定时器Timer的实现
    目录标准库中的Timer模拟实现Timer标准库中的Timer 标准库中有一个Timer类,java.util.Timer,核心方法为schedule,schedule有两个参数,第...
    99+
    2022-11-13
    Java 多线程 定时器Timer Java 定时器Timer Java 定时器
  • Java 中Timer和TimerTask 定时器和定时任务使用的例子
    这两个类使用起来非常方便,可以完成我们对定时器的绝大多数需求Timer类是用来执行任务的类,它接受一个TimerTask做参数Timer有两种执行任务的模式,最常用的是schedule,它可以以两种方式执行任务:1:在某个时间(Data),...
    99+
    2023-05-31
    timertask 定时器 tim
  • 深入了解Java定时器中的Timer的原理
    目录主要成员变量定时功能TimerThread结论Demo代码位置Java在1.3版本引入了Timer工具类,它是一个古老的定时器,搭配TimerTask和TaskQueue一起使用...
    99+
    2022-11-12
  • 一文带你搞懂Java定时器Timer的使用
    目录一、定时器是什么二、自定义定时器一、定时器是什么 定时器类似于我们生活中的闹钟,可以设定一个时间来提醒我们。 而定时器是指定一个时间去执行一个任务,让程序去代替人工准时操作。 标...
    99+
    2023-01-09
    Java定时器Timer使用 Java定时器Timer Java定时器 Java Timer
  • java中Timer定时器的使用和启动方式
    目录Timer定时器的使用和启动1.概述2.应用场景3.使用方法4.启动方法java的几种定时器小结1.@Scheduled注解2.quartz3.使用Timer4.使用线程控制Ti...
    99+
    2022-11-12
  • golang定时器Timer的用法和实现原理解析
    目录一文搞懂golang定时器Timer的用法和实现原理前言Timertimer结构体创建定时器停止定时器重置定时器实现原理数据结构runtimeTimer创建Timer停止Time...
    99+
    2023-05-15
    golang定时器 golang定时器Ticker
  • Java中的CyclicBarrier源码分析
    这篇文章主要介绍了Java中的CyclicBarrier源码分析的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇Java中的CyclicBarrier源码分析文章都会有所收获,下面我们一起来看看吧。CyclicB...
    99+
    2023-06-30
  • java 中Buffer源码的分析
    java 中Buffer源码的分析BufferBuffer的类图如下:除了Boolean,其他基本数据类型都有对应的Buffer,但是只有ByteBuffer才能和Channel交互。只有ByteBuffer才能产生Direct的buffe...
    99+
    2023-05-31
    java buffer源码 buf
  • Android定时器Timer的停止和重启实现代码
    本文介绍了Android定时器Timer的停止和重启实现代码,分享给大家,具体如下:7月份做了一个项目,利用自定义控件呈现一幅动画,当时使用定时器来控制时间,但是当停止开启时总是出现问题。一直在寻找合理的方法解决这个问题,一直没有找到,最近...
    99+
    2023-05-30
    重启 android 定时器
  • Java源码分析:Guava之不可变集合ImmutableMap的源码分析
    目录一、案例场景二、ImmutableMap源码分析总结一、案例场景 遇到过这样的场景,在定义一个static修饰的Map时,使用了大量的put()方法赋值,就类似这样—— pu...
    99+
    2022-11-12
  • Java源码ConcurrentHashMap的示例分析
    小编给大家分享一下Java源码ConcurrentHashMap的示例分析,希望大家阅读完这篇文章之后都有所收获,下面让我们一起去探讨吧!一、记录形式打算直接把过程写在源码中,会按序进行注释,查阅的时候可以按序号只看注释部分二、Concur...
    99+
    2023-06-15
  • Java ConcurrentHashMap的源码分析详解
    目录概述ForwardingNode节点TreeNodeTreeBinSizeCtl初始化初始化流程查找插入扩容红黑树的读&写读操作写操作小结容器计数总结概述 Concurr...
    99+
    2023-03-02
    Java ConcurrentHashMap源码 Java ConcurrentHashMap
  • Java源码解析之ConcurrentHashMap的示例分析
    小编给大家分享一下Java源码解析之ConcurrentHashMap的示例分析,希望大家阅读完这篇文章之后都有所收获,下面让我们一起去探讨吧!早期 ConcurrentHashMap,其实现是基于:分离锁,也就是将内部进行分段(Segme...
    99+
    2023-06-15
  • java中CopyOnWriteArrayList源码的示例分析
    这篇文章将为大家详细讲解有关java中CopyOnWriteArrayList源码的示例分析,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。简介CopyOnWriteArrayList是ArrayList的...
    99+
    2023-06-29
  • Java中Handler源码的示例分析
    这篇文章主要介绍了Java中Handler源码的示例分析,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。从很早开始就认识到 Handler 了,只不过那时修为尚浅,了解的不够深...
    99+
    2023-06-02
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作