定时/计划功能在移动开发领域使用较多,比如Android技术。定时计划任务功能在Java中主要使用的就是Timer对象,它在内部使用多线程的方式进行处理,所以它和线程技术还是有非常大的关联的。
在JDK库中Timer类主要负责计划任务的功能,也就是在指定的时间开始执行某一个任务。
Timer类的主要作用就是设置计划任务,但封装任务的类却是TimerTask类
执行计划任务的代码要放人TimerTask的子类中,因为TimerTask是一个抽象类。
schedule (TimerTask task, Date time)
该方法的作用是在指定的日期执行一次某一任务。
schedule(TimerTack task, Date firstTime, long period)
该方法的作用是在指定的日期之后,按指定的间隔周期性地无限循环地执行某一任务。
schedule(TimerTask task, long delay):
该方法的作用是以执行schedule(TimerTask task, longdelay)方法当前的时间为参考时间,在此时间基础上延迟指定的毫秒数后执行一次TimerTask任务。
schedule(TimerTask task, long delay, long period)的测试
该方法的作用是以执行schedule(TimerTask task, long delay, long period)方法当前的时间为参考时间,在此时间基础上延迟指定的毫秒数,再以某一间隔时间无限次数地执行某一任务。
scheduleAtFixedRate(TimerTask task, Date firstTime, long period)
方法schedule和scheduleAtFixedRate都会顺序序执行,所以不要考虑非线程安全的情况。
方法schedule和scheduleAtFixedRate主要的区别只在于不延时的情况。
使用schedule方法:如果执行任务的时间没有被延时,那么下一次任务的执行时间参考的是上一次任务的“开始”时的时间来计算。
使用scheduleAtFixedRate方法:如果执行任务的时间没有被延时,那么下一次任务的执行时间参考的是上一次任务的“结束”时的时间来计算。
延时的情况则没有区别,也就是使用schedule或scheduleAtFixedRate方法都是如果执行任务的时间被延时,那么下一次任务的执行时间参考的是上一次任务“结束”时的时间来计算。
schedule方法没有具有任务追赶执行性,而scheduleAtFixedRate是有的。
1、方法schedule (TimerTask task, Date time)的测试
该方法的作用是在指定的日期执行一次某一任务。
1.1、执行任务的时间晚于当前时间:在未来执行的效果
public class Run {
//private static Timer timer = new Timer(true);
private static Timer timer = new Timer();
public static class MyTask extends TimerTask {
@Override
public void run() {
System.out.println("运行了!时间为: " + new Date());
}
}
public static void main(String[] args) throws ParseException {
MyTask task = new MyTask();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String dateString = "2016-5-8 11:44:11";
Date date = sdf.parse(dateString);
System.out.println("字符串时间为: " + date.toLocaleString()
+", 当前时间为: "+ new Date().toLocaleString() );
timer.schedule(task, date);
}
}
字符串时间为: 2016-5-8 11:44:11, 当前时间为: 2016-5-8 11:43:44 运行了!时间为: Sun May 08 11:44:11 CST 2016 |
任务虽然执行完了,但进行还未销毁,呈红色状态,为什么会出现这样的情况?
在创建Timer对象时,JDK源代码为
public Timer() {此构造方法调用的是如下构造方法
this("Timer-" + serialNumber());
}
public Timer(String name) {查看构造方法可以得知,创建一个Timer就是启动一个新的线程,这个新启动的线程并不是守护线程,它一直在运行。
thread.setName(name);
thread.start();
}
下一步将新创建的Timer改成守护线程,就是将Timer(true)就行了。结果
字符串时间为: 2016-5-8 15:40:11, 当前时间为: 2016-5-8 15:39:40 |
1.2、计划时间早于当前时间:提前运行的效果
如果执行任务的时间早于当前时间,则立即执行task任务。
public class RunTimerIsDaemon {
private static Timer timer = new Timer();
public static class MyTask extends TimerTask {
@Override
public void run() {
System.out.println("运行了!时间为: " + new Date());
}
}
public static void main(String[] args) throws ParseException {
MyTask task = new MyTask();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String dateString = "2015-5-8 15:40:11"; //这里把时间设为过去时
Date date = sdf.parse(dateString);
System.out.println("字符串时间为: " + date.toLocaleString()
+", 当前时间为: "+ new Date().toLocaleString() );
timer.schedule(task, date);
}
}
字符串时间为: 2015-5-8 15:40:11, 当前时间为: 2016-5-8 15:55:57 运行了!时间为: Sun May 08 15:55:57 CST 2016 |
1.3、多个TImerTask任务及延时的测试
Timer中允许有多个TimerTask任务
public class Run {
private static Timer timer = new Timer();
public static class MyTask1 extends TimerTask {
@Override
public void run() {
System.out.println("task1运行了!时间为: " + new Date());
}
}
public static class MyTask2 extends TimerTask {
@Override
public void run() {
System.out.println("task2运行了!时间为: " + new Date());
}
}
public static void main(String[] args) throws ParseException {
MyTask1 task1 = new MyTask1();
MyTask2 task2 = new MyTask2();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String dateString1 = "2016-5-8 16:29:11"; //这里把时间设为未来时
String dateString2 = "2016-5-8 16:29:20"; //这里把时间设为未来时
Date date1 = sdf.parse(dateString1);
Date date2 = sdf.parse(dateString2);
System.out.println("字符串时间为: " + date1.toLocaleString()
+ ", 当前时间为: " + new Date().toLocaleString());
System.out.println("字符串时间为: " + date2.toLocaleString()
+ ", 当前时间为: " + new Date().toLocaleString());
timer.schedule(task1, date1);
timer.schedule(task2, date2);
}
}
字符串时间为: 2016-5-8 16:29:11, 当前时间为: 2016-5-8 16:29:10 字符串时间为: 2016-5-8 16:29:20, 当前时间为: 2016-5-8 16:29:10 task1运行了!时间为: Sun May 08 16:29:11 CST 2016 task2运行了!时间为: Sun May 08 16:29:20 CST 2016 |
public class Run {
private static Timer timer = new Timer();
public static class MyTask1 extends TimerTask {
@Override
public void run() {
try {
System.out.println("1 begin 运行了!时间为: " + new Date());
Thread.sleep(20000);
System.out.println("1 end 运行了!时间为: " + new Date());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static class MyTask2 extends TimerTask {
@Override
public void run() {
System.out.println("2 begin 运行了!时间为: " + new Date());
System.out.println("运行了!时间为 : " + new Date());
System.out.println("2 end 运行了!时间为: " + new Date());
}
}
public static void main(String[] args) throws ParseException {
MyTask1 task1 = new MyTask1();
MyTask2 task2 = new MyTask2();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String dateString1 = "2016-5-8 18:52:11"; //这里把时间设为未来时
String dateString2 = "2016-5-8 18:52:20"; //这里把时间设为未来时
Date date1 = sdf.parse(dateString1);
Date date2 = sdf.parse(dateString2);
System.out.println("字符串时间为: " + date1.toLocaleString()
+ ", 当前时间为: " + new Date().toLocaleString());
System.out.println("字符串时间为: " + date2.toLocaleString()
+ ", 当前时间为: " + new Date().toLocaleString());
timer.schedule(task1, date1);
timer.schedule(task2, date2);
}
}
字符串时间为: 2016-5-8 18:52:11, 当前时间为: 2016-5-8 18:51:40 字符串时间为: 2016-5-8 18:52:20, 当前时间为: 2016-5-8 18:51:40 1 begin 运行了!时间为: Sun May 08 18:52:11 CST 2016 1 end 运行了!时间为: Sun May 08 18:52:31 CST 2016 2 begin 运行了!时间为: Sun May 08 18:52:31 CST 2016 运行了!时间为 : Sun May 08 18:52:31 CST 2016 2 end 运行了!时间为: Sun May 08 18:52:31 CST 2016 |
------------------------------------------------------------------------------------------------
2、方法schedule(TimerTack task, Date firstTime, long period)的测试
该方法的作用是在指定的日期之后,按指定的间隔周期性地无限循环地执行某一任务。
2.1、计划时间晚于当前时间:在未来执行的效果
public class Run {
private static Timer timer = new Timer();
public static class MyTask extends TimerTask {
@Override
public void run() {
System.out.println("运行了!时间为: " + new Date());
}
}
public static void main(String[] args) throws ParseException {
MyTask task = new MyTask();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String dateString = "2016-5-8 19:20:11"; //这里把时间设为过去时
Date date = sdf.parse(dateString);
System.out.println("字符串时间为: " + date.toLocaleString()
+", 当前时间为: "+ new Date().toLocaleString() );
timer.schedule(task, date, 4000); //每隔4s运行一次TimerTask任务
}
}
字符串时间为: 2016-5-8 19:20:11, 当前时间为: 2016-5-8 19:20:05 运行了!时间为: Sun May 08 19:20:11 CST 2016 运行了!时间为: Sun May 08 19:20:15 CST 2016 运行了!时间为: Sun May 08 19:20:19 CST 2016 运行了!时间为: Sun May 08 19:20:23 CST 2016 。。。。。。 |
2.2、计划时间早于当前时间:提前运行的效果
如果计划时间早于当前时间,则立即执行task任务。把上面的程序再执行一次就达到了想要的时间,因为dateString已经是过去时了。
字符串时间为: 2016-5-8 19:20:11, 当前时间为: 2016-5-8 19:25:48 运行了!时间为: Sun May 08 19:25:48 CST 2016 运行了!时间为: Sun May 08 19:25:52 CST 2016 运行了!时间为: Sun May 08 19:25:56 CST 2016 。。。。。。 |
如果任务的执行时间(用sleep可以测试)长于预期时间,则任务会被延迟,但还是一个一个顺序执行。
2.3、TimerTask类的cancel()方法
TimerTask类中的cancel()方法的作用是将自身从任务队列中清除。
public class Run {
private static Timer timer = new Timer();
public static class MyTask1 extends TimerTask {
@Override
public void run() {
System.out.println("任务1运行了!时间为: " + new Date());
this.cancel(); //将自身从任务队列中清除
}
}
public static class MyTask2 extends TimerTask {
@Override
public void run() {
System.out.println("任务2运行了!时间为 : " + new Date());
}
}
public static void main(String[] args) throws ParseException {
MyTask1 task1 = new MyTask1();
MyTask2 task2 = new MyTask2();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String dateString = "2016-5-8 19:20:11"; //这里把时间设为过去时
Date date = sdf.parse(dateString);
System.out.println("字符串时间为: " + date.toLocaleString()
+", 当前时间为: "+ new Date().toLocaleString() );
timer.schedule(task1, date, 4000);
timer.schedule(task2, date, 4000);
}
}
字符串时间为: 2016-5-8 19:20:11, 当前时间为: 2016-5-8 19:39:45 任务1运行了!时间为: Sun May 08 19:39:45 CST 2016 任务2运行了!时间为 : Sun May 08 19:39:45 CST 2016 任务2运行了!时间为 : Sun May 08 19:39:49 CST 2016 任务2运行了!时间为 : Sun May 08 19:39:53 CST 2016 |
2.4、Timer类的cancel()方法
和TimerTask类中的cancel()方法清除自身不同,Timer类中的cancel()方法的作用是将任务队列中的全部任务清空。将上面的MyTask1修改为如下:
public static class MyTask1 extends TimerTask {
@Override
public void run() {
System.out.println("任务1运行了!时间为: " + new Date());
timer.cancel(); //将任务队列中的全部任务清空
}
}
字符串时间为: 2016-5-8 19:20:11, 当前时间为: 2016-5-8 19:45:49 任务1运行了!时间为: Sun May 08 19:45:49 CST 2016 |
但也要注意:Timer类中的cancel()有时并一定人停下执行计划任务,而是正常执行,因为有时会争抢不到queue锁。
----------------------------------------------------
3、方法schedule(TimerTask task, long delay)的测试
该方法的作用是以执行schedule(TimerTask task, longdelay)方法当前的时间为参考时间,在此时间基础上延迟指定的毫秒数后执行一次TimerTask任务。
public class Run {
private static Timer timer = new Timer();
public static class MyTask extends TimerTask {
@Override
public void run() {
System.out.println("运行了!时间为: " + new Date());
}
}
public static void main(String[] args) throws ParseException {
MyTask task = new MyTask();
System.out.println("当前时间为: " + new Date().toLocaleString());
timer.schedule(task, 4000); //延迟4s后才执行task
}
}
当前时间为: 2016-5-8 19:58:29 运行了!时间为: Sun May 08 19:58:34 CST 2016 |
4、方法schedule(TimerTask task, long delay, long period)的测试
该方法的作用是以执行schedule(TimerTask task, long delay, long period)方法当前的时间为参考时间,在此时间基础上延迟指定的毫秒数,再以某一间隔时间无限次数地执行某一任务示例:将上面的main方法里的:
timer.schedule(task, 4000);
修改为
timer.schedule(task, 4000,1000); //延迟4s后再以1s的间隔无限次执行task
当前时间为: 2016-5-8 20:05:59 运行了!时间为: Sun May 08 20:06:03 CST 2016 运行了!时间为: Sun May 08 20:06:04 CST 2016 运行了!时间为: Sun May 08 20:06:05 CST 2016 ...... |
5、方法scheduleAtFixedRate(TimerTask task, Date firstTime, long period)的测试
方法schedule和scheduleAtFixedRate都会顺序序执行,所以不要考虑非线程安全的情况。
方法schedule和scheduleAtFixedRate主要的区别只在于不延时的情况。
使用schedule方法:如果执行任务的时间没有被延时,那么下一次任务的执行时间参考的是上一次任务的“开始”时的时间来计算。
使用scheduleAtFixedRate方法:如果执行任务的时间没有被延时,那么下一次任务的执行时间参考的是上一次任务的“结束”时的时间来计算。
延时的情况则没有区别,也就是使用schedule或scheduleAtFixedRate方法都是如果执行任务的时间被延时,那么下一次任务的执行时间参考的是上一次任务“结束”时的时间来计算。
5.1.测试schedule方法任务不延时
需要注意:下面的这个程序run里的睡眠时间要 小于 timer.schedule里的period时间
public class Run {
private static Timer timer = new Timer();
private static int runCount = 0;
public static class MyTask1 extends TimerTask {
@Override
public void run() {
try {
System.out.println("1 begin 运行了!时间为: " + new Date());
Thread.sleep(1000);
System.out.println("1 end 运行了!时间为: " + new Date());
runCount++;
if (runCount == 5) {
timer.cancel();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) throws ParseException {
MyTask1 task1 = new MyTask1();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String dateString1 = "2016-5-8 20:23:11"; //这里时间随意,最好用过去时
Date date1 = sdf.parse(dateString1);
System.out.println("字符串1时间为: " + date1.toLocaleString()
+ ", 当前时间为: " + new Date().toLocaleString());
timer.schedule(task1, date1, 3000);
}
}
字符串1时间为: 2016-5-8 20:23:11, 当前时间为: 2016-5-8 20:23:35 1 begin 运行了!时间为: Sun May 08 20:23:35 CST 2016 1 end 运行了!时间为: Sun May 08 20:23:36 CST 2016 1 begin 运行了!时间为: Sun May 08 20:23:38 CST 2016 1 end 运行了!时间为: Sun May 08 20:23:39 CST 2016 1 begin 运行了!时间为: Sun May 08 20:23:41 CST 2016 1 end 运行了!时间为: Sun May 08 20:23:42 CST 2016 |
5.2.测试schedule方法任务延时
把上面程序任务里的睡眠时间修改成5000进行测试,结果为:
字符串1时间为: 2016-5-8 20:23:11, 当前时间为: 2016-5-8 20:28:29 1 begin 运行了!时间为: Sun May 08 20:28:29 CST 2016 1 end 运行了!时间为: Sun May 08 20:28:34 CST 2016 1 begin 运行了!时间为: Sun May 08 20:28:34 CST 2016 1 end 运行了!时间为: Sun May 08 20:28:39 CST 2016 1 begin 运行了!时间为: Sun May 08 20:28:39 CST 2016 1 end 运行了!时间为: Sun May 08 20:28:44 CST 2016 |
5.3.测试scheduleAtFixedRate方法任务不延时
下面的这个程序run里的睡眠时间要 小于 scheduleAtFixedRate里的period时间
public class Run {
private static Timer timer = new Timer();
private static int runCount = 0;
public static class MyTask1 extends TimerTask {
@Override
public void run() {
try {
System.out.println("1 begin 运行了!时间为: " + new Date());
Thread.sleep(1000);
System.out.println("1 end 运行了!时间为: " + new Date());
runCount++;
if (runCount == 5) {
timer.cancel();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) throws ParseException {
MyTask1 task1 = new MyTask1();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String dateString1 = "2016-5-8 20:23:11"; //这里时间随意,最好用过去时
Date date1 = sdf.parse(dateString1);
System.out.println("字符串1时间为: " + date1.toLocaleString()
+ ", 当前时间为: " + new Date().toLocaleString());
timer.scheduleAtFixedRate(task1, date1, 2000);
}
}
字符串1时间为: 2016-5-8 20:23:11, 当前时间为: 2016-5-8 20:32:44 1 begin 运行了!时间为: Sun May 08 20:32:44 CST 2016 1 end 运行了!时间为: Sun May 08 20:32:45 CST 2016 1 begin 运行了!时间为: Sun May 08 20:32:45 CST 2016 1 end 运行了!时间为: Sun May 08 20:32:46 CST 2016 1 begin 运行了!时间为: Sun May 08 20:32:46 CST 2016 |
5.4.测试scheduleAtFixedRate方法任务延时
把上面程序任务里的睡眠时间修改成5000进行测试,结果为:
字符串1时间为: 2016-5-8 20:23:11, 当前时间为: 2016-5-8 20:40:18 1 begin 运行了!时间为: Sun May 08 20:40:18 CST 2016 1 end 运行了!时间为: Sun May 08 20:40:23 CST 2016 1 begin 运行了!时间为: Sun May 08 20:40:23 CST 2016 1 end 运行了!时间为: Sun May 08 20:40:28 CST 2016 1 begin 运行了!时间为: Sun May 08 20:40:28 CST 2016 |
5.5、验证schedule方法不具有追赶执行性
public class Run {
private static Timer timer = new Timer();
public static class MyTask1 extends TimerTask {
@Override
public void run() {
System.out.println("1 begin 运行了!时间为: " + new Date());
System.out.println("1 end 运行了!时间为: " + new Date());
}
}
public static void main(String[] args) throws ParseException {
MyTask1 task1 = new MyTask1();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String dateString1 = "2016-5-8 20:46:11"; //这里时间随意,最好用过去时
Date date1 = sdf.parse(dateString1);
System.out.println("字符串1时间为: " + date1.toLocaleString()
+ ", 当前时间为: " + new Date().toLocaleString());
timer.schedule(task1, date1, 2000);
}
}
字符串1时间为: 2016-5-8 20:46:11, 当前时间为: 2016-5-8 20:48:28 1 begin 运行了!时间为: Sun May 08 20:48:28 CST 2016 1 end 运行了!时间为: Sun May 08 20:48:28 CST 2016 1 begin 运行了!时间为: Sun May 08 20:48:30 CST 2016 1 end 运行了!时间为: Sun May 08 20:48:30 CST 2016 1 begin 运行了!时间为: Sun May 08 20:48:32 CST 2016 |
5.6.验证scheduleAtFixedRate方法具有追赶执行性
将上面main方法里的schedule修改为scheduleAtFixedRate即可。结果为把两个时间段内所对应的Task伤”补充性“执行了,这就是Task任务的追赶的特性