深入理解Java多线程(五)

关于java多线程的概念以及基本用法:java多线程基础

5,定时器Timer

JDK中Timer类主要是负责计划任务的功能,也就是在指定的时间开始执行某一个任务,封装任务的类是TimerTask类,执行计划任务的代码要放进TimerTask的子类,因为它一个抽象类

5.1,方法schedule(TimerTask task,Date time)的测试

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

  • schedule(TimerTask task, long delay)
    以当前时间为基准,延迟指定的毫秒后执行一次TimerTask任务。

  • schedule(TimerTask task, Date time)
    在指定的日期执行一次TimerTask任务。

如果日期time早于当前时间,则立刻执行
如果日期time晚于当前时间,等到time那个时间点再执行

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

public class hkjh {
    private static Timer timer = new Timer();

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

    public static void main(String[] args) throws Exception
    {
        MyTask task = new MyTask();
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        String dateString = "2018-8-22 20:51:00";
        Date dateRef = sdf.parse(dateString);
        System.out.println("字符串时间:" + dateRef.toLocaleString() + " 当前时间:" + new Date().toLocaleString());
        timer.schedule(task, dateRef);
    }
}
字符串时间:2018-8-22 20:51:00 当前时间:2018-8-22 20:50:45
运行了!时间为:Wed Aug 22 20:51:00 CST 2018

等到了time再执行

2,计划时间早于当前时间:提前运行的效果

若 String dateString = “2018-8-22 19:51:00”;
结果为:

字符串时间:2018-8-22 19:51:00 当前时间:2018-8-22 20:52:18
运行了!时间为:Wed Aug 22 20:52:18 CST 2018

说明是立即执行

3,多个TimerTask任务及延时

public class Run2 {
    private static Timer timer = new Timer();

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

    public static void main(String[] args) throws Exception
    {
        MyTask task1 = new MyTask();
        MyTask task2 = new MyTask();
        SimpleDateFormat sdf1 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        SimpleDateFormat sdf2 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        String dateString1 = "2018-8-23 9:51:00";
        String dateString2 = "2018-8-23 9:51:02";
        Date dateRef1 = sdf1.parse(dateString1);
        Date dateRef2 = sdf2.parse(dateString2);
        System.out.println("字符串时间:" + dateRef1.toLocaleString() + " 当前时间:" + new Date().toLocaleString());
        System.out.println("字符串时间:" + dateRef2.toLocaleString() + " 当前时间:" + new Date().toLocaleString());
        timer.schedule(task1, dateRef1);
        timer.schedule(task2, dateRef2);
    }
}

结果:

字符串时间:2018-8-23 9:51:00 当前时间:2018-8-23 9:50:40
字符串时间:2018-8-23 9:51:02 当前时间:2018-8-23 9:50:40
运行了!时间为:Thu Aug 23 09:51:00 CST 2018
运行了!时间为:Thu Aug 23 09:51:02 CST 2018

可以看到,运行时间和设置的时间一致,证明了未来可以执行多个任务。另外注意,Task是以队列的方式一个一个被顺序执行的,所以执行的时间有可能和预期的时间不一致,因为前面的任务可能消耗过长,后面任务的运行时间也有可能被延迟。

假如任务1计划10:00:00被执行,任务2计划10:00:10被执行,结果任务1执行了30秒,那么任务2将在10:00:30被执行,因为Task是被放入队列中的,因此必须一个一个顺序运行。

5.2,方法schedule(TimerTask task,Date firsttime,long period)的测试

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

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

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

    public static void main(String[] args) throws Exception{
        try {
            MyTask task = new MyTask();
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            String dateString = "2018-8-24 9:18:00";
            Timer timer = new Timer();
            Date dateRef = sdf.parse(dateString);
            System.out.println("字符串时间:" + dateRef.toLocaleString() + " 当前时间:" + new Date().toLocaleString());
            timer.schedule(task, dateRef, 4000);
        }catch(ParseException e) {
            e.printStackTrace();
        }

    }
}

结果:

字符串时间:2018-8-24 9:18:00 当前时间:2018-8-24 9:17:31
运行了!时间为:Fri Aug 24 09:18:00 CST 2018
运行了!时间为:Fri Aug 24 09:18:04 CST 2018
运行了!时间为:Fri Aug 24 09:18:08 CST 2018
运行了!时间为:Fri Aug 24 09:18:12 CST 2018
运行了!时间为:Fri Aug 24 09:18:16 CST 2018
运行了!时间为:Fri Aug 24 09:18:20 CST 2018
运行了!时间为:Fri Aug 24 09:18:24 CST 2018
。。。
。。。

计划时间2018-8-24 9:18:00晚于当前时间2018-8-24 9:17:31,在到了计划时间后,每隔4秒执行一次run()方法

2,计划时间早于当前时间:立即执行
如果计划时间早于当前时间,则立即执行
更改上面的代码

String dateString = "2018-8-24 8:18:00";

结果:

字符串时间:2018-8-24 8:18:00 当前时间:2018-8-24 9:25:55
运行了!时间为:Fri Aug 24 09:25:55 CST 2018
运行了!时间为:Fri Aug 24 09:25:59 CST 2018
运行了!时间为:Fri Aug 24 09:26:03 CST 2018
运行了!时间为:Fri Aug 24 09:26:07 CST 2018
运行了!时间为:Fri Aug 24 09:26:11 CST 2018
。。。
。。。

计划时间2018-8-24 8:18:00早于当前时间2018-8-24 9:25:55,所以立即执行run()方法,每隔4秒再次执行

3,任务执行时被延迟

上面的run方法都是假设执行时间小于long period的,如果run方法执行时间大于period则执行run()方法的间隔变成了run方法的执行时间

5.3,TimerTask类的cancel方法

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

public class Run2 {
    static public class MyTaskA extends TimerTask{
        public void run(){
            System.out.println("A运行了!时间为:" + new Date());
            this.cancel();
        }
    }

    static public class MyTaskB extends TimerTask{
        public void run(){
            System.out.println("B运行了!时间为:" + new Date());
        }
    }

    public static void main(String[] args) throws ParseException{
        MyTaskA taskA = new MyTaskA();
        MyTaskB taskB = new MyTaskB();
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        String dateString = "2018-8-24 9:10:00";
        Timer timer = new Timer();
        Date dateRef = sdf.parse(dateString);
        System.out.println("字符串时间:" + dateRef.toLocaleString() + " 当前时间:" + new Date().toLocaleString());
        timer.schedule(taskA, dateRef, 4000);
        timer.schedule(taskB, dateRef, 4000);    
    }
}

结果:

字符串时间:2018-8-24 9:10:00 当前时间:2018-8-24 9:39:07
A运行了!时间为:Fri Aug 24 09:39:07 CST 2018
B运行了!时间为:Fri Aug 24 09:39:07 CST 2018
B运行了!时间为:Fri Aug 24 09:39:11 CST 2018
B运行了!时间为:Fri Aug 24 09:39:15 CST 2018

显然MyTaskA被清除了

5.4,Timer类的cancel方法

与上面的cancel()方法不同,Timer的cancel()可以将任务队列中的所有任务清空,但是如果cancel()方法没有争抢到queue锁,那么TimerTask类中的任务还是会继续执行

public class Run3 {
    //static Timer timer = new Timer();
    static int i=0;
    static public class MyTask extends TimerTask{
        @Override
        public void run() {
            System.out.println("正常执行了"+i);
            //timer.cancel();   
        }   
    }
    public static void main(String[] args) {
        while(true) {
            try {
                i++;
                Timer timer = new Timer();
                MyTask task = new MyTask();
                SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                String dateString = "2018-8-24 9:10:00";
                Date dateRef = sdf.parse(dateString);
                timer.schedule(task, dateRef,1000);
                timer.cancel();
            } catch (ParseException e) {
                e.printStackTrace();
            }
        }
    }
}

结果:

正常执行了95
正常执行了142
正常执行了222
正常执行了254
正常执行了330
正常执行了389
正常执行了393

类似的还有其他的方法:

  • 方法schedule(TimerTask task, long delay)以执行此方法的当前时间为参考时间,在此基础上延迟指定的毫秒后执行一次TimerTask任务
  • 方法schedule(TimerTask task, long delay, long period),以执行此方法的当前时间为参考时间,在此时间基础上延迟指定毫秒数,再每隔指定时间无限循环执行run()方法
  • 方法scheduleAtFixedRate(TimerTask task, Date firstTime, long period),在延时的场景下,schedule方法和scheduleAtFixedRate方法没有区别,它们的区别只是在非延时上。如果执行任务的时间没有被延时,对于schedule方法来说,下一次任务执行的时间参考的是上一次任务的开始时间来计算的;对于scheduleAtFixedRate方法来说,下一次任务执行的时间参考的是上一次任务的结束时间来计算的

猜你喜欢

转载自blog.csdn.net/qq_37438740/article/details/81914532