定时器Timer与TimerTask的使用

一:简介

在JDK类库中Timer主要负责计划任务的功能,也就是在指定时间执行某一任务,执行时候会在主线程之外起一个单独的线程执行指定的任务。该类主要是设置任务计划,但封装的类是TimerTask类。
TimerTask是一个实现了Runnable接口的抽象类,代表一个可被执行的任务,执行任务的代码要放在其子类中(TimerTask是抽象类)。

二:Timer整体类图

在这里插入图片描述

TimerTask类:主要为定时任务的具体内容。
Timer类中含有3个类:Timer、TimerThread、TaskQueue。
Timer类主要是设置定时任务,配置用户期望的任务执行时间、执行次数、执行内容
TimerThread类为Thread的扩展类,会一直从TaskQueue中获取下标为1的TimerTask进行执行。并根据该TimerTask是否需要重复执行来决定是否放回到TaskQueue中。
TaskQueue中存放一些列将要执行的TimerTask,以数组的形式存放,下标约小(注:下标为0不处理,即使用的最小下标为1),则表明优先级越高。
public class Timer {
    //默认给线程命名:"Timer-" + serialNumber()
	public Timer() {
        this("Timer-" + serialNumber());
    }
    //指定Timer的名字
    public Timer(String name) {
        thread.setName(name);
        thread.start();
    }
    //isDaemon:是否为守护线程
    public Timer(boolean isDaemon) {
        this("Timer-" + serialNumber(), isDaemon);
    }
    //给定线程名字,守护线程与否
    public Timer(String name, boolean isDaemon) {
        thread.setName(name);
        //setDaemon是指定线程是否为守护线程,true是守护线程,当主线程退出,      守护线程退出
        thread.setDaemon(isDaemon);
        thread.start();
    }    
}

//TimerThread类 继承Thread
class TimerThread extends Thread{
	private void mainLoop() {
	......
	//每次执行下标为1的TimerTask
	task = queue.getMin();
	......
	}
}

//TaskQueue 类
class TaskQueue {
	//TaskQueue 中存储TimerTask
    private TimerTask[] queue = new TimerTask[128];
    //返回数组中下标1的TimerTask
    TimerTask getMin() {
        return queue[1];
    }
}

三:Timer常用方法

1.Timer 类void schedule(TimerTask task, Date time)方法

1.task是定时任务,time是task任务执行时间.如果time时间早于当前则立即执行,否则在time时间执行。
2.task只执行一次,且执行完Timer线程任务不结束,因为Timer不是守护线程,Timer timer = new Timer(true);
是指定Timer 为守护线程,当task执行结束后,Timer 线程会立即结束。
3.Timer 可以执行多个定时任务,TimerTask 将会按照队列的方式一个一个被执行,如果前面的任务耗时长,后面的任务执行时间将会相应的延后。
public class TimerDemo {
	//指定Timer为守护线程,run方法结束Timer线程会结束。
    private static Timer timer = new Timer(true);
    //TimerTask 具体的定时任务
    static public class MyTask extends TimerTask {
        @Override
        public void run() {
            System.out.println("已运行,时间为:" + new Date());
        }
    }

    public static void main(String[] args) {
    	//创建定时任务
        MyTask myTask = new MyTask();
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        //早于当前时间,定时任务会立即执行
        String dateString = "2020-06-01 09:56:00";
        try {
            Date taskDate = sdf.parse(dateString);
            //启动定时任务
            timer.schedule(myTask,taskDate);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
已运行,时间为:Wed Jul 01 14:48:06 CST 2020

2.Timer 类void schedule(TimerTask task, Date firstTime, long period)方法

TimerTask任务在指定 firstTime任务之后,周期性每隔period时间,无限期循环的执行某一任务。

3.TimerTask 类boolean cancel()方法

TimerTask 类boolean cancel()方法,是将自身任务从任务队列中移除。

public class TimerDemo {
    private static Timer timer = new Timer();
    static public class MyTaskA extends TimerTask {
        @Override
        public void run() {
        	//只输出一次,MyTaskA 任务将会移除
            System.out.println("MyTaskA已运行,时间为:" + new Date());
            //执行TimerTask的cancel方法,将自身任务从队列中移除
            this.cancel();
        }
    }

    static public class MyTaskB extends TimerTask {
        @Override
        public void run() {
        	//移除MyTaskA任务,MyTaskB 任务不受影响
            System.out.println("MyTaskB已运行,时间为:" + new Date());
        }
    }

    public static void main(String[] args) {
        MyTaskA myTaskA = new MyTaskA();
        MyTaskB myTaskB = new MyTaskB();

        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        String dateString = "2020-07-01 15:19:00";
        try {
            Date taskDate = sdf.parse(dateString);
            timer.schedule(myTaskA,taskDate,2000);
            timer.schedule(myTaskB,taskDate,2000);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
MyTaskA已运行,时间为:Wed Jul 01 15:24:47 CST 2020
MyTaskB已运行,时间为:Wed Jul 01 15:24:47 CST 2020
MyTaskB已运行,时间为:Wed Jul 01 15:24:49 CST 2020
MyTaskB已运行,时间为:Wed Jul 01 15:24:51 CST 2020
MyTaskB已运行,时间为:Wed Jul 01 15:24:53 CST 2020
MyTaskB已运行,时间为:Wed Jul 01 15:24:55 CST 2020
MyTaskB已运行,时间为:Wed Jul 01 15:24:57 CST 2020
MyTaskB已运行,时间为:Wed Jul 01 15:24:59 CST 2020

4.Timer 类void cancel()方法

Timer 类void cancel()方法与TimerTask 类boolean cancel()方法将自身任务从任务队列中移除不同,它是将所有的任务都从队列中移除。

public class TimerDemo {

    private static Timer timer = new Timer();

    static public class MyTaskA extends TimerTask {
        @Override
        public void run() {
            System.out.println("MyTaskA已运行,时间为:" + new Date());
            //执行Timer类的cancel方法,将Timer中所有任务从队列中移除
            timer.cancel();
        }
    }

    static public class MyTaskB extends TimerTask {
        @Override
        public void run() {
            //MyTaskB也会被移除,因为MyTaskA与MyTaskB是队列执行,先执行MyTaskA再执行MyTaskB
            //MyTaskA中走了timer.cancel(),所以MyTaskB不会执行
            System.out.println("MyTaskB已运行,时间为:" + new Date());
        }
    }

    public static void main(String[] args) {
        MyTaskA myTaskA = new MyTaskA();
        MyTaskB myTaskB = new MyTaskB();

        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        String dateString = "2020-07-01 15:19:00";
        try {
            Date taskDate = sdf.parse(dateString);
            timer.schedule(myTaskA,taskDate,2000);
            timer.schedule(myTaskB,taskDate,2000);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
MyTaskA已运行,时间为:Wed Jul 01 16:20:48 CST 2020

源码:

public void cancel() {
        synchronized(queue) {
            thread.newTasksMayBeScheduled = false;
            queue.clear();
            queue.notify();  // In case queue was already empty.
        }
    }

执行cancel需要抢占queue锁,如果抢不到,则TimerTask 任务会继续执行。

5.Timer 类 void schedule(TimerTask task, long delay)方法

该方法以schedule(TimerTask task, long delay)方法的执行时间为基准,在此时间上延迟指定的毫秒数delay后执行一次TimerTask 任务。

6.Timer 类 void schedule(TimerTask task, long delay, long period)方法

该方法以schedule(TimerTask task, long delay, long period)方法的执行时间为基准,在此时间上延迟指定的毫秒数delay后,执行TimerTask 任务,之后间隔period毫秒无限循环执行TimerTask 任务。

7.Timer 类void scheduleAtFixedRate(TimerTask task, Date firstTime, long period)方法

TimerTask任务在指定 firstTime任务之后,周期性每隔period时间,无限期循环的执行某一任务。

四:重点:scheduleAtFixedRate(TimerTask task, Date firstTime, long period)与 schedule(TimerTask task, Date firstTime, long period)方法区别

1.相同点:两个方法都是按照顺序执行,无线程安全问题
2.任务不延时相同点:如果任务执行时间小于任务时间间隔,则第二次任务的开始时间是任务开始时间+period
3.任务延时相同点:如果任务执行时间大于任务时间间隔,导致第二次任务开始时候,上一个任务还未结束,则第二次任务的开始时间是在第一次任务结束时候立即执行。

scheduleAtFixedRate方法与schedule方法的追赶性问题

scheduleAtFixedRate方法具有追赶性!!!!如果计划执行时间早于当前时间,执行scheduleAtFixedRate方法后立即执行定时任务,根据任务间隔时间与当前时间减去计划执行时间,补上之前少执行的任务。
schedule方法不具有追赶性。!!!!

public class TimerDemo {

    private static Timer timer = new Timer();

    static public class MyTaskA extends TimerTask {
        @Override
        public void run() {
            System.out.println("MyTaskA已运行,时间为:" + new Date());
            try {
                //任务执行时间是1s
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
                
            }
            System.out.println("MyTaskA已结束,时间为:" + new Date());
        }
    }

    public static void main(String[] args) {
        MyTaskA myTaskA = new MyTaskA();
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        String dateString = "2020-07-02 14:31:00";
        try {
            Date taskDate = sdf.parse(dateString);
            //3s执行一次任务
            timer.scheduleAtFixedRate(myTaskA,taskDate,3000);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
MyTaskA已运行,时间为:Thu Jul 02 14:34:38 CST 2020
MyTaskA已结束,时间为:Thu Jul 02 14:34:39 CST 2020
MyTaskA已运行,时间为:Thu Jul 02 14:34:39 CST 2020
MyTaskA已结束,时间为:Thu Jul 02 14:34:40 CST 2020
MyTaskA已运行,时间为:Thu Jul 02 14:34:40 CST 2020
MyTaskA已结束,时间为:Thu Jul 02 14:34:41 CST 2020
MyTaskA已运行,时间为:Thu Jul 02 14:34:41 CST 2020
MyTaskA已结束,时间为:Thu Jul 02 14:34:42 CST 2020
MyTaskA已运行,时间为:Thu Jul 02 14:34:42 CST 2020
MyTaskA已结束,时间为:Thu Jul 02 14:34:43 CST 2020
MyTaskA已运行,时间为:Thu Jul 02 14:34:43 CST 2020
MyTaskA已结束,时间为:Thu Jul 02 14:34:44 CST 2020
MyTaskA已运行,时间为:Thu Jul 02 14:34:44 CST 2020
MyTaskA已结束,时间为:Thu Jul 02 14:34:45 CST 2020
MyTaskA已运行,时间为:Thu Jul 02 14:34:45 CST 2020
MyTaskA已结束,时间为:Thu Jul 02 14:34:46 CST 2020
MyTaskA已运行,时间为:Thu Jul 02 14:34:46 CST 2020

由上述执行结果可知,在Thu Jul 02 14:34:38 CST 2020启动定时任务后,没有间隔3s再执行下一次任务,而是任务结束就立即执行,这是因为任务计划执行时间早于当前时间,需要将这期间没有执行的任务补上。

五: 总结

java中可以使用定时任务的功能,针对不同的定时任务可以根据不同的API进行处理,从本质来说改技术仍然属于多线程,基于多线程实现,不管用再多的框架实现定时器功能,请不要忘了这事java基础,框架实现的基础。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值