当前位置: 首页 > news >正文

java timer定时执行一次_Java Timer(定时调用、实现固定时间执行)

而定义TimerThread部分的是:

看到这里知道了,Timer内部包装了一个线程,用来做独立于外部线程的调度,而TimerThread是一个default类型的,默认情况下是引用不到的,是被Timer自己所使用的。

接下来看下有那些属性

除了上面提到的thread,还有一个很重要的属性是:

private TaskQueue queue = new TaskQueue();

看名字就知道是一个队列,队列里面可以先猜猜看是什么,那么大概应该是我要调度的任务吧,先记录下了,接下来继续向下看:

里面还有一个属性是:threadReaper, 它是Object类型,只是重写了finalize方法而已,是为了垃圾回收的时候,将相应的信息回收掉,做GC的回补,也就是当timer线程由于某种 原因死掉了,而未被cancel,里面的队列中的信息需要清空掉,不过我们通常是不会考虑这个方法的,所以知道java写这个方法是干什么的就行了。

接下来看调度方法的实现:

对于上面6个调度方法,我们不做一一列举,为什么等下你就知道了:

来看下方法:

public void schedule(TimerTask task, long delay)

的源码如下:

1 public void schedule(TimerTask task, long delay) {

2        if (delay < 0)

3            throw new IllegalArgumentException("Negative delay.");

4        sched(task, System.currentTimeMillis()+delay, 0);

5    }

这里调用了另一个方法,将task传入,第一个参数传入System.currentTimeMillis()+delay可见为第一次需要执行的时间的 时间点了(如果传入Date,就是对象.getTime()即可,所以传入Date的几个方法就不用多说了),而第三个参数传入了0,这里可以猜下要么是 时间片,要么是次数啥的,不过等会就知道是什么了;另外关于方法:sched的内容我们不着急去看他,先看下重载的方法中是如何做的

再看看方法:

public void schedule(TimerTask task, long delay,long period)

源码为:

public void schedule(TimerTask task, long delay, long period) {

if (delay < 0)

throw new IllegalArgumentException("Negative delay.");

if (period <= 0)

throw new IllegalArgumentException("Non-positive period.");

sched(task, System.currentTimeMillis()+delay, -period);

}

看来也调用了方法sched来完成调度,和上面的方法唯一的调度时候的区别是增加了传入的period,而第一个传入的是0,所以确定这个参数为时间片, 而不是次数,注意这个里的period加了一个负数,也就是取反,也就是我们开始传入1000,在调用sched的时候会变成-1000,其实最终阅读完 源码后你会发现这个算是老外对于一种数字的理解,而并非有什么特殊的意义,所以阅读源码的时候也有这些困难所在。

最后再看个方法是:

public void scheduleAtFixedRate(TimerTasktask,long delay,long period)

源码为:

public void scheduleAtFixedRate(TimerTask task, long delay, long period) {

if (delay < 0)

throw new IllegalArgumentException("Negative delay.");

if (period <= 0)

throw new IllegalArgumentException("Non-positive period.");

sched(task, System.currentTimeMillis()+delay, period);

}

唯一的区别就是在period没有取反,其实你最终阅读完源码,上面的取反没有什么特殊的意义,老外不想增加一个参数来表示 scheduleAtFixedRate,而scheduleAtFixedRate和schedule的大部分逻辑代码一致,因此用了参数的范围来作为 区分方法,也就是当你传入的参数不是正数的时候,你调用schedule方法正好是得到scheduleAtFixedRate的功能,而调用 scheduleAtFixedRate方法的时候得到的正好是schedule方法的功能,呵呵,这些讨论没什么意义,讨论实质和重点:

来看sched方法的实现体:

private void sched(TimerTask task, long time, long period) {

if (time < 0)

throw new IllegalArgumentException("Illegal execution time.");

synchronized(queue) {

if (!thread.newTasksMayBeScheduled)

throw new IllegalStateException("Timer already cancelled.");

synchronized(task.lock) {

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();

}

}

queue为一个队列,我们先不看他数据结构,看到他在做这个操作的时候,发生了同步,所以在timer级别,这个是线程安全的,最后将task相关的参数赋值,主要包含nextExecutionTime(下一次执行时间),period(时间片),state(状态),然后将它放入queue队列中,做一次notify操作,为什么要做notify操作呢?看了后面的代码你就知道了。

简言之,这里就是讲task放入队列queue的过程,此时,你可能对queue的结构有些兴趣,那么我们先来看看queue属性的结构TaskQueue:

class TaskQueue {

private TimerTask[] queue = new TimerTask[128];

private int size = 0;

可见,TaskQueue的结构很简单,为一个数组,加一个size,有点像ArrayList,是不是长度就128呢,当然不 是,ArrayList可以扩容,它可以,只是会造成内存拷贝而已,所以一个Timer来讲,只要内部的task个数不超过128是不会造成扩容的;内部 提供了add(TimerTask)、size()、getMin()、get(int)、removeMin()、quickRemove(int)、 rescheduleMin(long newTime)、isEmpty()、clear()、fixUp()、fixDown()、heapify();

实践部分:

1、通过继承TimerTask的方式实现

必须重写run方法.

public class MyTask extends TimerTask

{

@Override

public void run()

{

SimpleDateFormat sdf = null;

sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");

System.out.println("当前时间:" + sdf.format(new Date()));

}

}

public class TestTask

{

public static void main(String[] args)

{

Timer t = new Timer(); // 建立Timer对象

MyTask task = new MyTask(); //定义任务

t.schedule(task, 1000,2000);//设置任务的执行,1秒后开始,每2秒执行一次

Calendar cal = Calendar.getInstance();

cal.set(Calendar.MINUTE, 30);

t.schedule(task, cal.getTime() , 2000);

}

}

2、通过匿名内部类实现

Timer timer = new Timer();

timer.scheduleAtFixedRate(new TimerTask() {

public void run() {

System.out.println("abc");

}

}, 1000 , 1000);

22/2<12

相关文章:

  • java字串数组_java字符串数组
  • java swing 拖拽文件夹_Java Swing 鼠标拖放文件 代码1
  • java treemap 降序排序_Java TreeMap 升序|降序排列
  • java 流关闭顺序_JAVA的节点流和处理流以及流的关闭顺序
  • java 多层结构故障_多层构架在实践中一些问题
  • java项目提高安全性_Java线程安全与程序性能
  • mysql 获取真是执行计划_Oracle 从缓存里面查找真实的执行计划
  • mysql 越文_mysql数据库乱码之保存越南文乱码解决方法_MySQL
  • java发布_java项目发布的方式
  • python分享的代码怎么写_【图片】分享一段功能非常简陋的python代码实现下载free种【pt吧】_百度贴吧...
  • android js调用java_如何在Android平台上使用JS直接调用Java方法
  • python实现并发和并行的方式有哪些_Python中的并行和并发是什么
  • java a a=null_面试题((A)null).fun()——java中null值的强转
  • java获取hashcode_java identityHashCode 和 hashCode
  • django mysql 加锁_Django数据库事务和死锁
  • IE9 : DOM Exception: INVALID_CHARACTER_ERR (5)
  • express.js的介绍及使用
  • Java 11 发布计划来了,已确定 3个 新特性!!
  • Laravel 实践之路: 数据库迁移与数据填充
  • Linux gpio口使用方法
  • Spark VS Hadoop:两大大数据分析系统深度解读
  • Vue2.0 实现互斥
  • Windows Containers 大冒险: 容器网络
  • 工程优化暨babel升级小记
  • 基于 Babel 的 npm 包最小化设置
  • 今年的LC3大会没了?
  • 目录与文件属性:编写ls
  • 前端技术周刊 2019-01-14:客户端存储
  • 让你的分享飞起来——极光推出社会化分享组件
  • 使用Maven插件构建SpringBoot项目,生成Docker镜像push到DockerHub上
  • 用jQuery怎么做到前后端分离
  • const的用法,特别是用在函数前面与后面的区别
  • 关于Kubernetes Dashboard漏洞CVE-2018-18264的修复公告
  • ​Python 3 新特性:类型注解
  • #、%和$符号在OGNL表达式中经常出现
  • #预处理和函数的对比以及条件编译
  • (1)(1.13) SiK无线电高级配置(五)
  • (51单片机)第五章-A/D和D/A工作原理-A/D
  • (done) 两个矩阵 “相似” 是什么意思?
  • (Java)【深基9.例1】选举学生会
  • (第8天)保姆级 PL/SQL Developer 安装与配置
  • (附源码)python旅游推荐系统 毕业设计 250623
  • (附源码)ssm高校志愿者服务系统 毕业设计 011648
  • (黑马出品_高级篇_01)SpringCloud+RabbitMQ+Docker+Redis+搜索+分布式
  • (实战)静默dbca安装创建数据库 --参数说明+举例
  • ***原理与防范
  • **登录+JWT+异常处理+拦截器+ThreadLocal-开发思想与代码实现**
  • ./configure、make、make install 命令
  • .CSS-hover 的解释
  • .NET CORE 2.0发布后没有 VIEWS视图页面文件
  • .Net CoreRabbitMQ消息存储可靠机制
  • .NET MVC第五章、模型绑定获取表单数据
  • .NET 指南:抽象化实现的基类
  • .NET/C# 判断某个类是否是泛型类型或泛型接口的子类型
  • .Net7 环境安装配置