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

最近需要用到定时调用的功能。可以通过java的Timer类来进行定时调用,下面是有关Timer的一些相关知识。

其实就Timer来讲就是一个调度器,而TimerTask呢只是一个实现了run方法的一个类,而具体的TimerTask需要由你自己来实现,例如这样:

Timer timer = newTimer();

timer.schedule(newTimerTask() {public voidrun() {

System.out.println("11232");

}

},200000 , 1000);

这里直接实现一个TimerTask(当然,你可以实现多个TimerTask,多个TimerTask可以被一个Timer会被分配到多个 Timer中被调度,后面会说到Timer的实现机制就是说内部的调度机制),然后编写run方法,20s后开始执行,每秒执行一次,当然你通过一个 timer对象来操作多个timerTask,其实timerTask本身没什么意义,只是和timer集合操作的一个对象,实现它就必然有对应的run 方法,以被调用,他甚至于根本不需要实现Runnable,因为这样往往混淆视听了,为什么呢?也是本文要说的重点。

在说到timer的原理时,我们先看看Timer里面的一些常见方法:

1、这个方法是调度一个task,经过delay(ms)后开始进行调度,仅仅调度一次。

public void schedule(TimerTask task, long delay)

2、在指定的时间点time上调度一次。

public void schedule(TimerTask task, Date time)

3、这个方法是调度一个task,在delay(ms)后开始调度,每次调度完后,最少等待period(ms)后才开始调度。

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

4、和上一个方法类似,唯一的区别就是传入的第二个参数为第一次调度的时间。

public void schedule(TimerTask task, Date firstTime, long period)

5、调度一个task,在delay(ms)后开始调度,然后每经过period(ms)再次调度,貌似和方法:schedule是一样的,其实不然,后面你会根据源码看到,schedule在计算下一次执行的时间的时候,是通过当前时间(在任务执行前得到) + 时间片,而scheduleAtFixedRate方法是通过当前需要执行的时间(也就是计算出现在应该执行的时间)+ 时间片,前者是运行的实际时间,而后者是理论时间点,例如:schedule时间片是5s,那么理论上会在5、10、15、20这些时间片被调度,但是如果由于某些CPU征用导致未被调度,假如等到第8s才被第一次调度,那么schedule方法计算出来的下一次时间应该是第13s而不是第10s,这样有可能下次就越到20s后而被少调度一次或多次,而scheduleAtFixedRate方法就是每次理论计算出下一次需要调度的时间用以排序,若第8s被调度,那么计算出应该是第10s,所以它距离当前时间是2s,那么再调度队列排序中,会被优先调度,那么就尽量减少漏掉调度的情况。

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

6、方法同上,唯一的区别就是第一次调度时间设置为一个Date时间,而不是当前时间的一个时间片,我们在源码中会详细说明这些内容。

public void scheduleAtFixedRate(TimerTask task, Date firstTime,long period)

源码部分

首先看Timer的构造方法有几种:

构造方法1:无参构造方法,简单通过Tiemer为前缀构造一个线程名称:

publicTimer() {this("Timer-" +serialNumber());

}

创建的线程不为主线程,则主线程结束后,timer自动结束,而无需使用cancel来完成对timer的结束。

构造方法2:传入了是否为后台线程,后台线程当且仅当进程结束时,自动注销掉。

public Timer(booleanisDaemon) {this("Timer-" +serialNumber(), isDaemon);

}

另外两个构造方法负责传入名称和将timer启动:

public Timer(String name, booleanisDaemon) {

thread.setName(name);

thread.setDaemon(isDaemon);

thread.start();

}

这里有一个thread,这个thread很明显是一个线程,被包装在了Timer类中,我们看下这个thread的定义是:

private TimerThread thread = new TimerThread(queue);

而定义TimerThread部分的是:

而定义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, longdelay) {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, longperiod) {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, longperiod) {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, longperiod) {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 newIllegalStateException("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:

classTaskQueue {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 extendsTimerTask

{

@Overridepublic voidrun()

{

SimpleDateFormat sdf= null;

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

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

}

}

public classTestTask

{public static voidmain(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 = newTimer();

timer.scheduleAtFixedRate(newTimerTask() {public voidrun() {

System.out.println("abc");

}

},1000 , 1000);

致谢:感谢您的耐心阅读!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/263983.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

忧郁

网络确实是个好东西&#xff0c;她让我们知识丰富起来&#xff0c;让我们沟通更方便&#xff0c;让世界更小了。 博客确实是个好东西&#xff0c;她记录博主的成长。 但是最近经常被博客郁闷到&#xff0c;本来我就是一个忧郁的人&#xff0c;每当我看到有些博客更新了几年最后…

docker基础应用

环境&#xff1a; centos 7 docker 19.03.0-beta3 192.168.10.10 关于如何安装docker请参考&#xff1a;https://www.cnblogs.com/caesar-id/p/10857312.html 1、查找合适的版本 [rootlocalhost ~]# docker search centos 2、下载centos镜像 [rootlocalhost ~]#docker pull c…

永远的福气

你知不知道每个女人最想要的不是一生的美丽美丽会骗了我和你换不到永远的福气你知不知道每个女人最害怕的就是爱人骗自己以为他说的诺言会保证形影不离我碰见你 也看到我最想过的日子因为有你 更了解自己我相信你 却也相信白头到老不能靠运气你是否可以 爱我一生一世发生什么都…

正则表达式(不断更新,欢迎纠错)

匹配中文字符的正则表达式&#xff1a; [u4e00-u9fa5]评注&#xff1a;匹配中文还真是个头疼的事&#xff0c;有了这个表达式就好办了匹配双字节字符(包括汉字在内)&#xff1a;[^x00-xff]评注&#xff1a;可以用来计算字符串的长度&#xff08;一个双字节字符长度计2&#xff…

java 简单数据类型_java基本数据类型

基本数据类型JAVA中一共有八种基本数据类型&#xff0c;他们分别是byte、short、int、long、float、double、char、boolean类型型别字节取值范围byte整型1byte-27 ~ 27-1short整型2byte-215 ~ 215-1int整型4byte-231 ~ 231-1long整型8byte-263 ~ 263-1float浮点型4byte3.402823…

Postgresql日志配置

将PostgreSQL数据库安装后&#xff0c;需要进行一些关于数据库日志的配置&#xff0c;将postgresql.conf文件中&#xff0c;关于日志的配置选项详解&#xff0c;记录如下&#xff1a; 1.logging_collector on/off 是否将日志重定向至文件中&#xff0c;默认是off&#xff08;修…

Win7中IIS7.0安装及ASP环境配置

打开控制面板中“程序”: “程序”中“打开或关闭Windows功能”: 如图&#xff0c;安装IIS7时需要选择要使用的功能模块: IIS7安装完成之后可以在开始菜单的所有程序中看到“管理工具”&#xff0c;其中有一个“Internet信息服务管理器”&#xff0c;如果没有可以按以下步骤…

java 3number_java 数据Number、Math

一个初出茅庐的小子与大家共享一些关于Number和Math的使用&#xff0c;因水平有限&#xff0c;难免有写的不完善的地方&#xff0c;嘻嘻。看完之后&#xff0c;希望可以留下你珍贵的指导意见。The Numbers Classes在写代码的时候&#xff0c;也许会使用到java各种的基本数据类型…

HOG(方向梯度直方图)

结合这周看的论文,我对这周研究的Histogram of oriented gradients(HOG)谈谈自己的理解&#xff1a; HOG descriptors 是应用在计算机视觉和图像处理领域&#xff0c;用于目标检測的特征描写叙述器。这项技术是用来计算局部图像梯度的方向信息的统计值。这样的方法跟边缘方向直…

保留数据给硬盘增加分区

我的块硬盘只有一个分区&#xff0c;里面还有数据&#xff0c;但是想再划分一个区&#xff0c;在win10系统下是这样操作的 首先&#xff0c;我的硬盘的文件系统是fat32&#xff0c;先通过命令提示符把文件系统转换成NTFS&#xff0c;转换前“压缩卷”是灰色的 convert c: /fs:n…

Google Doc API研究之一:模拟页面上传任意类型文件

一直以来想要做个程序&#xff0c;将google doc用作网盘&#xff0c;程序做 的差不多了才发现不是所有的人都可以上传任意类型的文件&#xff0c;只有商业用户才可以。商业用户是要交钱的的&#xff0c;这与我们倡导的免费精神相关太远。怎么办&#xff0c;我的心血 不能白费&a…

java string fill_Java使用fill()数组填充的实现

Arrays 类提供了一个 fill() 方法&#xff0c;可以在指定位置进行数值填充。fill() 方法虽然可以填充数组&#xff0c;但是它的功能有限制&#xff0c;只能使用同一个数值进行填充。语法如下&#xff1a;声明举例&#xff1a;public static void fill(int[] a, form, to, int v…

FL-EM7688 Smart评估板openwrt开发环境搭建(linux固件部分)

搭建前先阅读原厂文档\FL-EM7688 Smart V1.0评估板1资料\文档\FL-EM7688 Smart评估板使用说明.pdf 1.根据FL-EM7688 Smart评估板使用说明.pdf安装好串口调试工具&#xff0c;以及实现通过网线更新固件 2.安装虚拟机&#xff08;版本号12.56&#xff09;和ubuntu-16.04.3-deskto…

小型公司如何管理

一直以来&#xff0c;人们对大型公司的管理都非常称道&#xff0c;对小型公司的管理都认为比较简单。这话说得有一定的道理&#xff0c;毕竟小型公司的人比较少&#xff0c;相对来说&#xff0c;管理的范围和直径比较小一些&#xff0c;能比较有效地执行和监管。但是&#xff0…

堆栈溢出从入门到提高

转自&#xff1a;http://www.jiaonan.net/html/2007/06/20070624034620915.htm 入门篇 2007-6-24 15:46:20 本讲的预备知识&#xff1a; 首先你应该了解intel汇编语言&#xff0c;熟悉寄存器的组成和功能。你必须有堆栈和存储分配方面 的基础知识&#xff0c;有关这方面的计…

java replaceall函数_JAVA中string.replace和string.replaceAll的区别及用法

展开全部JAVA中string.replace()和string.replaceAll()的区别及用法乍一看&#xff0c;字面上理解好像replace只替换第一个出现的字符(受javascript的影响)&#xff0c;32313133353236313431303231363533e59b9ee7ad9431333361313836replaceall替换所有的字符&#xff0c;其实大…

Linux之RPM 软件管理程序

RPM RPM是软件管理程序&#xff0c;提供软件的安装、升级、查询、反安装的功能。优点&#xff1a;a、安装方便&#xff0c;软件中所有数据都经过编译和打包b、查询、升级、反安装方便缺点&#xff1a;a、缺乏灵活性b、存在相依属性 用法&#xff1a; rpm 参数 软件包 指令选…

快意人生

仁者不忧&#xff0c; 智者不惑。 勇者不惧&#xff0c; 信者不悔&#xff1b; 锐气藏于胸&#xff0c; 和气浮于面&#xff0c; 才气见于事&#xff0c; 义气施于人&#xff1b; 有为有不为&#xff0c; 知足知不足&#xff01; 转载于:https://www.cnblogs.com/freeton/archi…

DOM Element

特点&#xff1a; 1. nodeType 的值为1. 2. nodeName 的值为元素的标签名(大写); 3. nodeValue 的值为null. 4. parentNode 可能是Element、Text、Comment、ProcessingInstruction、CDATASection、EntityReference。 例&#xff1a; <div id"div1">hha</di…

java filefilter的用法_Java File.listFiles(FileFilter filter)方法

Java Java File.listFiles(FileFilter filter)方法具有以下语法。public File [] listFiles(FileFilter filter)示例在下面的代码显示如何使用File.listFiles(FileFilter filter)方法。import java.io.File;import java.io.FileFilter;public class Main {public static void m…