java

关注公众号 jb51net

关闭
首页 > 软件编程 > java > Java定时器Timer与TimerTask

Java定时器Timer与TimerTask的使用详解

作者:细嗅蔷薇的大老虎

这篇文章主要介绍了Java定时器Timer与TimerTask的使用详解,在JDK类库中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基础,框架实现的基础。

到此这篇关于Java定时器Timer与TimerTask的使用详解的文章就介绍到这了,更多相关Java定时器Timer与TimerTask内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文