【探索】定时器Timer的使用

定时器Timer的使用

Time类的主要作用就是设置计划任务,但封装任务的类却是TimeTask类,执行计划任务的代码要放入TimerTask的子类中,因为TimeTask是一个抽象类。

1.方法schedule(TimeTask task,Date Time)的测试

该方法的作用是在指定的日期执行一次某一任务。

1.1 执行任务的时间晚于当前时间:在未来执行的效果

创建Run1.java类:

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.SortedSet;
import java.util.Timer;
import java.util.TimerTask;

public class Run1 {
    public static Timer timer = new Timer();
    static public class MyTask extends TimerTask{
        @Override
        public void run() {
            System.out.println("运行了!时间为:"+new Date());
        }
    }
    public static void main(String[] args) {
        try {
            MyTask task = new MyTask();
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            String dateString = "2020-06-29 15:10:00";
            Date dateRef = sdf.parse(dateString);
            System.out.println("字符串时间:"+dateRef+"当前时间:"+new Date());
            timer.schedule(task,dateRef);
        }catch (ParseException e){
            e.printStackTrace();
        }
    }
}

最后的运行结果为:

字符串时间:Mon Jun 29 15:10:00 CST 2020当前时间:Mon Jun 29 15:08:48 CST 2020
运行了!时间为:Mon Jun 29 15:10:00 CST 2020

任务虽然执行完毕,但是能看到进程还未销毁,呈红色状态。

实际上,创建一个Timer就是启动一个新的线程,这个新启动的线程并不是守护线程,它一直在运行。

所以解决这个办法,就是在创建Timer之后将他设值成一个守护线程

public class Run1 {

    public static Timer timer = new Timer(true);
    static public class MyTask extends TimerTask{
        @Override
        public void run() {
            System.out.println("运行了!时间为:"+new Date());
        }
    }

    public static void main(String[] args) {
        try {
            MyTask task = new MyTask();
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            String dateString = "2020-06-29 15:10:00";
            Date dateRef = sdf.parse(dateString);
            System.out.println("字符串时间:"+dateRef+"当前时间:"+new Date());
            timer.schedule(task,dateRef);
        }catch (ParseException e){
            e.printStackTrace();
        }
    }
}

1.2 执行任务的时间早于当前时间:则立即执行

1.3 多个TimerTask及延时的测试

Timer允许有多个TimerTask任务。

创建新的Run2类:

public class Run2 {
    public static Timer timer = new Timer();
    static public class MyTask1 extends TimerTask{
        @Override
        public void run() {
            System.out.println("task1运行了!时间为:"+new Date());
        }
    }
    static public class MyTask2 extends TimerTask{
        @Override
        public void run() {
            System.out.println("task2运行了!时间为:"+new Date());
        }
    }
    public static void main(String[] args) {
        try {
            MyTask1 task = new MyTask1();
            MyTask2 task2 = new MyTask2();
            SimpleDateFormat sdf1 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            SimpleDateFormat sdf2 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            String dateString1 = "2020-06-29 15:31:00";
            String dateString2 = "2020-06-29 15:31:00";
            Date dateRef1 = sdf1.parse(dateString1);
            Date dateRef2 = sdf2.parse(dateString2);
            System.out.println("字符串1时间:"+dateRef1+"当前时间:"+new Date());
            System.out.println("字符串2时间:"+dateRef2+"当前时间:"+new Date());
            timer.schedule(task,dateRef1);
            timer.schedule(task2,dateRef2);
        }catch (ParseException e){
            e.printStackTrace();
        }
    }
}

运行结果为:

字符串1时间:Mon Jun 29 15:31:00 CST 2020当前时间:Mon Jun 29 15:31:06 CST 2020
字符串2时间:Mon Jun 29 15:31:00 CST 2020当前时间:Mon Jun 29 15:31:06 CST 2020
task1运行了!时间为:Mon Jun 29 15:31:06 CST 2020
task2运行了!时间为:Mon Jun 29 15:31:06 CST 2020

TimerTask是以队列的方式一个一个被顺序执行的,所以执行的时间有可能和预期的时间不一致,因为前面的任务有可能耗时时间较长,则后面的任务运行的时间也会被延迟。

2.方法schedule(TimerTask task, Date firstTime,long period)的测试

该方法的作用是在指定的日期之后,按指定的间隔周期性地无限循环地执行某一任务。

2.1 计划时间晚于当前时间:在未来执行的效果

修改Run1.java类如下:

public class Run1 {
    public static Timer timer = new Timer();
    static public class MyTask extends TimerTask{
        @Override
        public void run() {
            System.out.println("运行了!时间为:"+new Date());
        }
    }

    public static void main(String[] args) {
        try {
            MyTask task = new MyTask();
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            String dateString = "2020-06-29 15:50:00";
            Date dateRef = sdf.parse(dateString);
            System.out.println("字符串时间:"+dateRef+"当前时间:"+new Date());
            timer.schedule(task,dateRef,4000);
        }catch (ParseException e){
            e.printStackTrace();
        }
    }
}

运行结果如下:从运行结果来看,每隔4秒执行一次task任务,并且是无限期的重复执行。

字符串时间:Mon Jun 29 15:50:00 CST 2020当前时间:Mon Jun 29 15:50:17 CST 2020
运行了!时间为:Mon Jun 29 15:50:17 CST 2020
运行了!时间为:Mon Jun 29 15:50:21 CST 2020
运行了!时间为:Mon Jun 29 15:50:25 CST 2020

2.2 计划时间早于当前时间:立即执行

2.3 任务执行时间同样会有被延时的可能

3. TimerTask的Cancel方法

TimerTask类中的cancel()方法的作用是将自身从任务队列中清除。

新建Run3.java:

public class Run3 {
    public static Timer timer = new Timer();
    static public class MyTaskA extends TimerTask{
        @Override
        public void run() {
            System.out.println("A运行了!时间为:"+new Date());
            this.cancel();//取消自身
        }
    }
    static public class MyTaskB extends TimerTask{
        @Override
        public void run() {
            System.out.println("B运行了!时间为:"+new Date());
        }
    }
    public static void main(String[] args) {
        try {
            MyTaskA task = new MyTaskA();
            MyTaskB task2 = new MyTaskB();
            SimpleDateFormat sdf1 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            String dateString1 = "2020-06-29 15:56:00";
            Date dateRef2 = sdf1.parse(dateString1);
            System.out.println("字符串2时间:"+dateRef2+"当前时间:"+new Date());
            timer.schedule(task,dateRef2,4000);
            timer.schedule(task2,dateRef2,4000);
        }catch (ParseException e){
            e.printStackTrace();
        }
    }
}

最后的运行结果如下:A在之后的循环执行中不再执行

字符串2时间:Mon Jun 29 15:56:00 CST 2020当前时间:Mon Jun 29 15:56:51 CST 2020
A运行了!时间为:Mon Jun 29 15:56:51 CST 2020
B运行了!时间为:Mon Jun 29 15:56:51 CST 2020
B运行了!时间为:Mon Jun 29 15:56:55 CST 2020

4.Timer类的cancel()方法

Timer类的cancel()和TimerTask的cancel()不同,Timer类中的cancel()的作用是将任务队列中的全部任务清空。

修改Run3.java如下:

public class Run3 {
    public static Timer timer = new Timer();
    static public class MyTaskA extends TimerTask{
        @Override
        public void run() {
            System.out.println("A运行了!时间为:"+new Date());
            timer.cancel();//取消自身
        }
    }
    static public class MyTaskB extends TimerTask{
        @Override
        public void run() {
            System.out.println("B运行了!时间为:"+new Date());
        }
    }
    public static void main(String[] args) {
        try {
            MyTaskA task = new MyTaskA();
            MyTaskB task2 = new MyTaskB();
            SimpleDateFormat sdf1 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            String dateString1 = "2020-06-29 15:56:00";
            Date dateRef2 = sdf1.parse(dateString1);
            System.out.println("字符串2时间:"+dateRef2+"当前时间:"+new Date());
            timer.schedule(task,dateRef2,4000);
            timer.schedule(task2,dateRef2,4000);
        }catch (ParseException e){
            e.printStackTrace();
        }
    }
}

最后的运行结果如下,全部任务被清除,且进程被销毁,按钮变为灰色

字符串2时间:Mon Jun 29 15:56:00 CST 2020当前时间:Mon Jun 29 15:59:33 CST 2020
A运行了!时间为:Mon Jun 29 15:59:33 CST 2020

Timer类中的cancel()使用注意事项:

  • Timer类中的cancel()方法有时并不一定会停止执行计划任务,而是正常执行,这是因为Timer类中cancel()方法有时并没有争抢到Queue锁,所以TimerTask类中的任务可以正常被执行。

5.方法schedule(TimerTask task,long delay)方法的测试

该方法的作用是以执行schedule(TimerTask task,long delay)方法当前的时间为参考时间,在此时间基础上延迟指定的毫秒数执行一次TimerTask任务。

6.方法schedule(TimerTask task,long delay,long period)方法的测试

该方法的作用是以执行schedule(TimerTask task,long delay)方法当前的时间为参考时间,在此时间基础上延迟指定的毫秒数执行一次TimerTask任务,再以某一间隔时间无限次数地执行某一任务。

7.方法scheduleAtFixedRate(TimerTask task,Date firstTime,long period)方法的测试

  • 方法schedule和方法scheduleAtFixedRate都会按顺序执行,所以不用考虑非线程安全的情况

  • 方法schedule和方法scheduleAtFixedRate主要的区别只在于是否具有追赶性的情况

  • 使用schedule方法:如果执行任务的时间没有被延时,那么下一次任务的执行时间参考的是上一次任务的“开始”时间来计算。

  • 使用scheduleAtFixedRate方法:如果执行任务的时间没有被延时,那么下一次任务的执行时间参考的是上一次任务的“开始”时间来计算。

  • 延时的情况则没有区别,两者都是下一次执行的时间参考的都是上一次任务“结束”时的时间来计算。

7.1 测试schedule不延时的情况

创建Run4.java类:

public class Run4 {
    private static Timer timer = new Timer();
    private static int runCount = 0;
    static public class MyTask1 extends TimerTask{
        @Override
        public void run() {
            try {
                System.out.println("1运行了!时间为:"+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) {
        try {
            MyTask1 task = new MyTask1();
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            String dateString = "2020-06-29 15:50:00";
            Date dateRef = sdf.parse(dateString);
            System.out.println("字符串时间:"+dateRef+"当前时间:"+new Date());
            timer.schedule(task,dateRef,3000);
        }catch (ParseException e){
            e.printStackTrace();
        }
    }
}

最后的运行结果如下:在不延时的情况下,如果执行任务的时间没有被延时,则下一次执行任务的时间是上一次任务的开始时间加上delay的时间

字符串时间:Mon Jun 29 15:50:00 CST 2020当前时间:Mon Jun 29 16:14:23 CST 2020
1运行了!时间为:Mon Jun 29 16:14:23 CST 2020
 1 end 了!时间为:Mon Jun 29 16:14:24 CST 2020
1运行了!时间为:Mon Jun 29 16:14:26 CST 2020
 1 end 了!时间为:Mon Jun 29 16:14:27 CST 2020
1运行了!时间为:Mon Jun 29 16:14:29 CST 2020
 1 end 了!时间为:Mon Jun 29 16:14:30 CST 2020
1运行了!时间为:Mon Jun 29 16:14:32 CST 2020
 1 end 了!时间为:Mon Jun 29 16:14:33 CST 2020
1运行了!时间为:Mon Jun 29 16:14:35 CST 2020
 1 end 了!时间为:Mon Jun 29 16:14:36 CST 2020

7.2 测试schedule延时的情况

public class Run2 {
    private static Timer timer = new Timer();
    private static int runCount = 0;
    static public class MyTask1 extends TimerTask{
        @Override
        public void run() {
            try {
                System.out.println("1运行了!时间为:"+new Date());
                Thread.sleep(5000);
                System.out.println("1结束了!时间为:"+new Date());
                runCount++;
                if(runCount == 2){
                    timer.cancel();
                }
            }catch (InterruptedException e){
                e.printStackTrace();
            }
        }
    }
    public static void main(String[] args) {
        try {
            MyTask1 task = new MyTask1();
            SimpleDateFormat sdf1 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            String dateString1 = "2020-06-29 15:31:00";
            Date dateRef1 = sdf1.parse(dateString1);
            System.out.println("字符串1时间:"+dateRef1+"当前时间:"+new Date());
            timer.schedule(task,dateRef1,2000);
        }catch (ParseException e){
            e.printStackTrace();
        }
    }
}

最后的运行结果如下:如果任务被延时,那么下一次任务的执行时间以上一次任务“结束”时的时间来参考计算。

字符串1时间:Mon Jun 29 15:31:00 CST 2020当前时间:Mon Jun 29 16:26:28 CST 2020
1运行了!时间为:Mon Jun 29 16:26:28 CST 2020
1结束了!时间为:Mon Jun 29 16:26:33 CST 2020
1运行了!时间为:Mon Jun 29 16:26:33 CST 2020
1结束了!时间为:Mon Jun 29 16:26:38 CST 2020

7.3 测试scheduleAtFixedRate方法任务不延时的情况

修改Run4.java类如下:

public class Run4 {
    private static Timer timer = new Timer();
    private static int runCount = 0;
    static public class MyTask1 extends TimerTask{
        @Override
        public void run() {
            try {
                System.out.println("1运行了!时间为:"+new Date());
                Thread.sleep(1000);
                System.out.println(" 1 end 了!时间为:"+new Date());
                runCount++;
                if(runCount == 2){
                    timer.cancel();
                }
            }catch (InterruptedException e){
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] args) {
        try {
            MyTask1 task = new MyTask1();
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            String dateString = "2020-06-29 15:50:00";
            Date dateRef = sdf.parse(dateString);
            System.out.println("字符串时间:"+dateRef+"当前时间:"+new Date());
            timer.scheduleAtFixedRate(task,dateRef,3000);
        }catch (ParseException e){
            e.printStackTrace();
        }
    }
}

最后的运行结果为下:在不延时的情况下,如果执行任务的时间没有被延时,则下一次执行任务的时间是上一次任务的开始时间加上delay的时间

字符串时间:Mon Jun 29 15:50:00 CST 2020当前时间:Mon Jun 29 16:28:39 CST 2020
1运行了!时间为:Mon Jun 29 16:28:39 CST 2020
 1 end 了!时间为:Mon Jun 29 16:28:40 CST 2020
1运行了!时间为:Mon Jun 29 16:28:40 CST 2020
 1 end 了!时间为:Mon Jun 29 16:28:41 CST 2020

7.4 测试scheduleAtFixedRate方法任务延时的情况

修改Run2.java类如下:

public class Run2 {
    private static Timer timer = new Timer();
    private static int runCount = 0;
    static public class MyTask1 extends TimerTask{
        @Override
        public void run() {
            try {
                System.out.println("1运行了!时间为:"+new Date());
                Thread.sleep(5000);
                System.out.println("1结束了!时间为:"+new Date());
                runCount++;
                if(runCount == 2){
                    timer.cancel();
                }
            }catch (InterruptedException e){
                e.printStackTrace();
            }
        }
    }
    public static void main(String[] args) {
        try {
            MyTask1 task = new MyTask1();
            SimpleDateFormat sdf1 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            String dateString1 = "2020-06-29 15:31:00";
            Date dateRef1 = sdf1.parse(dateString1);
            System.out.println("字符串1时间:"+dateRef1+"当前时间:"+new Date());
            timer.scheduleAtFixedRate(task,dateRef1,2000);
        }catch (ParseException e){
            e.printStackTrace();
        }
    }
}

最后的运行结果如下:如果任务被延时,那么下一次任务的执行时间以上一次任务“开始”时的时间来参考计算。

字符串1时间:Mon Jun 29 15:31:00 CST 2020当前时间:Mon Jun 29 16:31:00 CST 2020
1运行了!时间为:Mon Jun 29 16:31:00 CST 2020
1结束了!时间为:Mon Jun 29 16:31:05 CST 2020
1运行了!时间为:Mon Jun 29 16:31:05 CST 2020
1结束了!时间为:Mon Jun 29 16:31:10 CST 2020

8.验证schedule不具有追赶性

创建Run6.java类如下:

public class Run6 {
    public static Timer timer = new Timer();
    static public class MyTask extends TimerTask{
        @Override
        public void run() {
            System.out.println("运行了!时间为:"+new Date());
            System.out.println("结束运行了!时间为:"+new Date());
        }
    }

    public static void main(String[] args) throws InterruptedException {
        MyTask myTask = new MyTask();
        System.out.println("现在执行时间为:" + new Date());
        Calendar c = Calendar.getInstance();
        c.set(Calendar.SECOND, c.get(Calendar.SECOND) +20);
        Date runDate = c.getTime();
        System.out.println("计划执行时间为:" + runDate);
        Timer timer = new Timer();
        //调用的是schedule方法,验证其不具追赶性
        timer.schedule(myTask, runDate, 4000);

    }
}

运行结果如下:Mon Jun 29 16:43:36 CST 2020到Mon Jun 29 16:43:56 CST 2020之间的时间被取消掉,不执行了,这就是Task任务不追赶。

现在执行时间为:Mon Jun 29 16:43:36 CST 2020
计划执行时间为:Mon Jun 29 16:43:56 CST 2020
运行了!时间为:Mon Jun 29 16:43:56 CST 2020
结束运行了!时间为:Mon Jun 29 16:43:56 CST 2020
运行了!时间为:Mon Jun 29 16:44:00 CST 2020
结束运行了!时间为:Mon Jun 29 16:44:00 CST 2020

9.验证scheduleAtFixedRate具有追赶性

修改Run6.java如下:

public class Run6 {
    public static Timer timer = new Timer();
    static public class MyTask extends TimerTask{
        @Override
        public void run() {
            System.out.println("运行了!时间为:"+new Date());
            System.out.println("结束运行了!时间为:"+new Date());
        }
    }

    public static void main(String[] args) throws InterruptedException {
        MyTask myTask = new MyTask();
        System.out.println("现在执行时间为:" + new Date());
        Calendar c = Calendar.getInstance();
        c.set(Calendar.SECOND, c.get(Calendar.SECOND) -20);
        Date runDate = c.getTime();
        System.out.println("计划执行时间为:" + runDate);
        Timer timer = new Timer();
        //调用的是schedule方法,验证其不具追赶性
        timer.scheduleAtFixedRate(myTask, runDate, 4000);
    }
}

最后运行结果如下:16:51:38-16:51:58的时间被补充执行:本来我的时间间隔为4s执行一次,但是前期没有按照我的时间间隔来执行。

追赶性就是如果我们设置任务开始的时间在当前时间之前,那么他会计算出两个时间的差,计算出时间差内能够执行任务多少次,然后立即执行,执行完这个次数之后再按照我定的时间间隔做

现在执行时间为:Mon Jun 29 17:05:07 CST 2020
计划执行时间为:Mon Jun 29 17:04:47 CST 2020
运行了!时间为:Mon Jun 29 17:05:07 CST 2020
结束运行了!时间为:Mon Jun 29 17:05:07 CST 2020
运行了!时间为:Mon Jun 29 17:05:07 CST 2020
结束运行了!时间为:Mon Jun 29 17:05:07 CST 2020
运行了!时间为:Mon Jun 29 17:05:07 CST 2020

当执行任务的时间大于周期间隔时,会发生什么呢?
(1)schedule方法:下一次执行时间相对于 上一次 实际执行完成的时间点 ,因此执行时间会不断延后
(2)scheduleAtFixedRate方法:下一次执行时间相对于上一次开始的 时间点 ,因此执行时间不会延后,存在并发性

使用schedule:下一次的执行时间点=上一次程序执行完成的时间点+间隔时间

使用scheduleAtFixedRate:下一次的执行时间点=上一次程序开始执行的时间点+间隔时间 ;并且如果间隔时间为5秒,因为前一个任务要执行>5秒,而当前任务已经开始执行了,因此两个任务间存在重叠,需要考虑线程同步

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章