Java多线程编程15 -Timer定时任务

本文详细探讨了Java中的定时器Timer及其相关方法,如schedule、scheduleAtFixedRate等。通过实例展示了如何设置定时任务,以及在任务执行时间超过预定间隔时的行为。特别是,scheduleAtFixedRate具有追赶性,当任务执行时间超过周期时,会尽快执行以赶上预定的时间间隔。
摘要由CSDN通过智能技术生成

在java中,定时计划任务功能主要使用的就是Timer对象,主要有如下技术点:

  • 实现指定事件执行指定任务
  • 实现按照指定周期执行任务

定时器Timer

Timer类的主要作用是设置计划执行的任务,而封装任务的类是TimerTask类,类声明如下:

public abstract class TimerTask implements Runnable{...}

可以看到TimerTask是一个抽象类,实现了Runnable接口,我们需要创建子类继承TimerTask抽象类,并且将要执行的任务放入这个子类中。

schedule(TimerTask task,Date time)

首先先来看一下Timer的最基本用法,schedule(TimerTask, Date),我们要先创建一个类继承TimerTask类,并重写run()方法,然后实例化这个类的对象,作为schedule(TimerTask, Date)方法的参数之一。另一个参数是我们需要TimerTask中任务执行的时间。另外,当两个任务都需要计划执行,并且是使用了同一个Timer实例

该方法的作用是在指定的日期到达时执行一次某一任务。这里分两种情况

  • 执行任务的任务晚于当前时间,在未来执行
  • 执行的时间早于当前时间,提前运行

在未来执行:

public class TimerTest {

    private static class MyTimerTask extends TimerTask {
        @Override
        public void run() {
            System.out.println("MyTimerTask runs and the time is : "+new Date());
        }
    }

    public static void main(String[] args) throws ParseException {
        MyTimerTask myTimerTask = new MyTimerTask();
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        // 这里设置的时间是在未来,此时时间到达该点的时候,会执行
        String dateString = "2020-01-06 20:36:00";
        Date date = sdf.parse(dateString);
        Timer timer = new Timer();
        timer.schedule(myTimerTask, date);
    }
}
多个TimerTask任务延时测试

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

public class Test {
	 private static class MyTimerTask1 extends TimerTask {
	        @Override
	        public void run() {
	            try {
	                System.out.println("MyTimerTask1 begin runs and the time is : "+new Date());
	                Thread.sleep(5000);
	                System.out.println("MyTimerTask1 end runs and the time is : "+new Date());
	            } catch (InterruptedException e) {
	                e.printStackTrace();
	            }
	        }
	    }

	    private static class MyTimerTask2 extends TimerTask {
	        @Override
	        public void run() {
	            System.out.println("MyTimerTask2 runs and the time is : "+new Date());
	        }
	    }

	    public static void main(String[] args) throws ParseException {
	        MyTimerTask1 myTimerTask1 = new MyTimerTask1();
	        MyTimerTask2 myTimerTask2 = new MyTimerTask2();
	        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
	        String dateString1 = "2020-01-06 20:49:00";// MyTimerTask1的起始执行时间
	        String dateString2 = "2020-01-06 20:49:03";// MyTimerTask2的起始执行时间
	        Date date1 = sdf.parse(dateString1);
	        Date date2 = sdf.parse(dateString2);
	        Timer timer = new Timer();
	        timer.schedule(myTimerTask1, date1);
	        timer.schedule(myTimerTask2, date2);
	    }
	}
	输出:
MyTimerTask1 begin runs and the time is : Mon Jan 06 20:49:02 CST 2020
MyTimerTask1 end runs and the time is : Mon Jan 06 20:49:07 CST 2020
MyTimerTask2 runs and the time is : Mon Jan 06 20:49:07 CST 2020

可以看到这里由于MyTimerTask1 执行需要花费5秒,那么将会影响task2的执行时间,task2以此时间为基准,向后延迟执行,因为task是被放入到队列中,一个一个顺序执行的,同步执行!

schedule(TimerTask task,Date firstTime,long period)

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

TimerTask.cancel()和Timer.cancel()

TimerTask.cancel()是用来将本任务从Timer的执行队列中删除掉,使之不再继续执行;

Timer.cancel()是将本Timer的所有任务都撤销,不再继续执行任何任务,同时进程会被销毁,程序结束。
但是有时调用cancel()方法的线程可能没有第一时间竞争到queue的对象锁,那么任务可能会完成执行,没有被及时销毁。

scheduleAtFixedRate(TimerTask task, Date firstTime, long period)

scheduleAtFixedRateschedule一样,在当前任务延时的情况,上一个任务执行完毕后当前任务立即执行,两个方法的处理方式都一样

所以schedule和scheduleAtFixedRate主要的区别只在于不延时的情况:

  • schedule方法:如果执行的任务时间没有被延时,那么下一次任务的执行时间参考的是上一次任务的开始时间来计算
  • scheduleAtFixedRate方法:如果执行任务的时间没有被延时,那么下一次任务的执行时间参考的是上一次任务结束的时间来计算 ????
public class Test {
	 private static Timer timer = new Timer();
	    private static class MyTimerTask1 extends TimerTask {

	        private int count = 0;

	        @Override
	        public void run() {
	            try {
	                System.out.println("MyTimerTask1 begin runs and the time is : "+new Date());
	                Thread.sleep(1000);
	                System.out.println("MyTimerTask1 end runs and the time is : "+new Date());
	                count++;
	                if (count > 3) {
	                    timer.cancel(); // 停止
	                }

	            } catch (InterruptedException e) {
	                e.printStackTrace();
	            }
	        }
	    }

	    public static void main(String[] args) throws ParseException {
	        MyTimerTask1 myTimerTask1 = new MyTimerTask1();
	        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
	        String dateString = "2020-01-06 21:03:50";// MyTimerTask1的起始执行时间
	        Date date = sdf.parse(dateString);
	        timer.schedule(myTimerTask1, date,3000); //从"2020-01-06 21:03:50"开始,每隔3秒执行一次
	    }
	}
输出: 可以看到,在没有延时情况下,下一次任务开始时间 = 上一层任务开始时间 + 延时delay
MyTimerTask1 begin runs and the time is : Mon Jan 06 21:03:50 CST 2020
MyTimerTask1 end runs and the time is : Mon Jan 06 21:03:51 CST 2020
MyTimerTask1 begin runs and the time is : Mon Jan 06 21:03:53 CST 2020
MyTimerTask1 end runs and the time is : Mon Jan 06 21:03:54 CST 2020
MyTimerTask1 begin runs and the time is : Mon Jan 06 21:03:56 CST 2020
MyTimerTask1 end runs and the time is : Mon Jan 06 21:03:57 CST 2020
MyTimerTask1 begin runs and the time is : Mon Jan 06 21:03:59 CST 2020
MyTimerTask1 end runs and the time is : Mon Jan 06 21:04:00 CST 2020

schedule 换成 scheduleAtFixedRate 结果时一样的????

scheduleAtFixedRate 追赶性

把上面代码中任务的执行时间dateString换成当前时间之前: 当前时间是2020-01-06 21:25:09

 public static void main(String[] args) throws ParseException {
	        MyTimerTask1 myTimerTask1 = new MyTimerTask1();
	        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
	        String dateString = "2020-01-06 21:25:00";// MyTimerTask1的起始执行时间
	        Date date = sdf.parse(dateString);
	        timer.schedule(myTimerTask1, date,3000); 
} 
输出:
MyTimerTask1 begin runs and the time is : Mon Jan 06 21:25:10 CST 2020
MyTimerTask1 end runs and the time is : Mon Jan 06 21:25:11 CST 2020
MyTimerTask1 begin runs and the time is : Mon Jan 06 21:25:13 CST 2020
MyTimerTask1 end runs and the time is : Mon Jan 06 21:25:14 CST 2020
MyTimerTask1 begin runs and the time is : Mon Jan 06 21:25:16 CST 2020
MyTimerTask1 end runs and the time is : Mon Jan 06 21:25:17 CST 2020

可以发现,在当前时间和执行时间的缝隙里,任务没有执行,在从当前时间开始之后,还是按照3s执行一次。
再来看看 把schedule方法 换成 scheduleAtFixedRate

前几行:
MyTimerTask1 begin runs and the time is : Mon Jan 06 21:31:54 CST 2020
MyTimerTask1 end runs and the time is : Mon Jan 06 21:31:55 CST 2020
MyTimerTask1 begin runs and the time is : Mon Jan 06 21:31:55 CST 2020 // 不等待,直接执行
MyTimerTask1 end runs and the time is : Mon Jan 06 21:31:56 CST 2020
MyTimerTask1 begin runs and the time is : Mon Jan 06 21:31:56 CST 2020
MyTimerTask1 end runs and the time is : Mon Jan 06 21:31:57 CST 2020

看到 虽然任务还是从当前时间开始执行,但是没有停留3s执行下一次,而是执行完这一次,马上开始执行下一次。这就是一个追赶的过程,它 在把当前时间和执行时间的缝隙里欠的任务 补回来

等到补完了,就正常按照 3s 执行一次来:后几行如下:
MyTimerTask1 begin runs and the time is : Mon Jan 06 21:32:30 CST 2020
MyTimerTask1 end runs and the time is : Mon Jan 06 21:32:31 CST 2020
MyTimerTask1 begin runs and the time is : Mon Jan 06 21:32:33 CST 2020 // 注意是33才开始执行
MyTimerTask1 end runs and the time is : Mon Jan 06 21:32:34 CST 2020

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值