定时任务调度(Timer篇)

本文介绍了Java中的Timer和TimerTask类,用于简单的任务调度。Timer只有一个后台线程,因此不适合时效性要求高的多任务并发场景。Timer的schedule和scheduleAtFixedRate方法有不同的行为:schedule在任务延迟后会按实际完成时间计算,而scheduleAtFixedRate会尝试按固定间隔赶上进度,可能导致并发问题。文章还提到了两者的主要方法和使用示例,并指出了Timer的并发管理和异常处理缺陷。

Timer由JDK提供,可以实现简单的任务调度场景。Timer只有一个后台线程,所以在使用在注意其场合。定时器是异步执行,不影响定时器后面代码的运行!

虽然一个Timer可以运行多个定时任务,但是一个Timer是串行运行

Timer的使用禁区:1、对时效性要求比较较高的多任务并发作业    2、对复杂任务的调度

Timer的缺陷:1、管理并发的缺陷以及任务之间的协同缺陷(因为后台只有一个线程)  2、当任务抛出异常时的缺陷(所有task终止)同时也不支持例如每周三运行任务这样的模式

 

 

这里还介绍了Timer和TimerTask的几个常用方法

TimerTask的cancel、scheduledExecutionTime方法。

Timer的schedule、scheduleAtFixedRate、cancel、purge方法

 

Timer中schedule和scheduleAtFixedRate的区别:(具体用法见下面的demo)

  1. 首次计划执行时间早于当前的时间
  2. 任务执行所需时间超出任务的执行周期间隔

 

 

下面是Timer的使用:

Timer需要搭配TimerTask使用

TimerTask:

package timer;

import java.util.TimerTask;

/**
 * @Author Rhine
 * @Date 2019/1/21 0:44
 **/
public class MyTimerTask extends TimerTask {

    private String name;

    public MyTimerTask(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public void run() {
        //打印当前name的内容
        System.out.println("Current exce name is: "+name);

    }
}

 Timer:

package timer;

import java.util.Timer;

/**
 * @Author Rhine
 * @Date 2019/1/21 0:46
 **/
public class MyTimer {

    public static void main(String[] args) {

        //1.创建一个timer实例
        Timer timer=new Timer();
        //2.创建一个MyTimerTask实例
        MyTimerTask myTimerTask=new MyTimerTask("No.1");
        //3.通过timer定时定频率调用myTimerTask的业务逻辑
        // 即第一次执行实在当前时间的两秒之后,之后每隔一秒钟执行一次
        timer.schedule(myTimerTask,2000L,1000L);

    }
}

 

Timer有schedule和scheduleAtFixedRate两种方法

其中schedule的有四种用法,scheduleAtFixedRate有两种用法

package timer;

import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Timer;

/**
 * @Author Rhine
 * @Date 2019/1/21 0:46
 **/
public class MyTimer {

    public static void main(String[] args) {

        //1.创建一个timer实例
        Timer timer=new Timer();
        //2.创建一个MyTimerTask实例
        MyTimerTask myTimerTask=new MyTimerTask("No.1");
        //3.通过timer定时定频率调用myTimerTask的业务逻辑

        // 即第一次执行实在当前时间的两秒之后,之后每隔一秒钟执行一次
//        timer.schedule(myTimerTask,2000L,1000L);

        /**
         * 获取当前时间,并设置成距离当前时间三秒之后的时间
         * 如当前是2016-11-10 23:59:57
         * 则设置后的时间则为2016-11-11 00:00:00
         */
        Calendar calendar=Calendar.getInstance();
        SimpleDateFormat sf=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        System.out.println(sf.format(calendar.getTime()));
        calendar.add(Calendar.SECOND,3);

        //------------------------ schedule的四种用法 -------------------------------

        /**
         *  schedule(task,time)
         *  1.在时间等于或超过time的时候执行且仅执行一次task
         *   如在2016-11-11 00:00:00执行一次task:打印任务的名字
         */
//        myTimerTask.setName("schedule1");
//        timer.schedule(myTimerTask,calendar.getTime());



        /**
         *  schedule(task,time,period)
         *  2.时间等于或超过time时首次执行task,之后每隔period毫秒重复执行一次task
         *   如在2016-11-11 00:00:00执行一次task:打印任务的名字,之后每隔两秒执行一次task
         */
//        myTimerTask.setName("schedule2");
//        timer.schedule(myTimerTask,calendar.getTime(),2000);


        /**
         *  schedule(task,delay)
         *  3.等待delay毫秒后执行且仅执行一次task
         *  如在2016-11-11 00:00:01执行一次task:打印任务的名字
         */
//        myTimerTask.setName("schedule3");
//        timer.schedule(myTimerTask,1000);


        /**
         *  schedule(task,delay,period)
         *  4.等待delay毫秒后首次执行task,之后每隔period毫秒重复执行一次task
         */
//        myTimerTask.setName("schedule4");
//        timer.schedule(myTimerTask,3000,2000);




        //------------------------- scheduleAtFixedRate的两种用法 ------------------------------------
        /**
         *  1.scheduleAtFixedRate(task,time,period)
         *  时间等于或超过time时首次执行task,之后每隔period毫秒重复执行一次task
         *  如在2016-11-11 00:00:00执行一次task:打印任务的名字,之后每隔两秒执行一次task
         */
//        myTimerTask.setName("scheduleAtFixedRate1");
//        timer.scheduleAtFixedRate(myTimerTask,calendar.getTime(),2000);


        /**
         *  2.scheduleAtFixedRate(task,delay,period)
         *  等待delay毫秒后首次执行task,之后每隔period毫秒重复执行一次task
         */
//        myTimerTask.setName("scheduleAtFixedRate2");
//        timer.scheduleAtFixedRate(myTimerTask,3000,2000);


    }
}

Timer中schedule和scheduleAtFixedRate的区别:

区别一、首次计划执行时间早于当前的时间 

schedule  如果第一次执行时间被delay了,随后的执行时间按照上一次实际执行完成的时间点进行计算。(首次推迟就按推迟的时间开始)

scheduleAtFixedRate   如果第一次执行时间被delay了,随后的执行时间按照上一次开始的时间点进行计算,并且为了赶上进度会多次执行任务,因此TimerTask中的执行体需要考虑同步(首次推迟按照设定时间,赶上进度)

package timer;

import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Timer;
import java.util.TimerTask;

/**
 * 测试schedule和scheduleAtFixedRate的区别
 * @Author Rhine
 * @Date 2019/1/21 15:46
 **/
public class DifferenceTest {
    public static void main(String[] args) {
        //规定时间格式
        final SimpleDateFormat sf=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        //获取当前的具体时间
        Calendar calendar=Calendar.getInstance();
        System.out.println("Current time is: "+ sf.format(calendar.getTime()));

        //设置成6秒前的时间,若当前时间为2016-12-28 00:00:06,那么设置之后的时间为2016-12-28 00:00:00
        calendar.add(Calendar.SECOND,-6);
        Timer timer=new Timer();
        //第一次执行时间为6秒前,之后每隔两秒钟执行一次
        
        
//        timer.schedule(new TimerTask() {
//            @Override
//            public void run() {
//                //打印当前的计划执行时间
//                System.out.println("Scheduled exec time is: "+sf.format(scheduledExecutionTime()));
//                System.out.println("Task is being executed!");
//            }
//        },calendar.getTime(),2000);
        
        timer.scheduleAtFixedRate(new TimerTask() {
            @Override
            public void run() {
                //打印当前的计划执行时间
                System.out.println("Scheduled exec time is: "+sf.format(scheduledExecutionTime()));
                System.out.println("Task is being executed!");
            }
        },calendar.getTime(),2000);
    }
}

区别二、任务执行所需时间超出任务的执行周期间隔

    schedule  下一次执行时间相对于上一次实际完成的时间点,因此执行的时间会不断延后(第一次完成之后才开始第二次)

    scheduleAtFixedRate   下一次执行时间相对于上一次开始的时间点,因此执行时间一般不会延后,因此存在并发性(按第一次开始的时间的period后开始第二次,不论第一次是否完成)

package timer;

import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Timer;
import java.util.TimerTask;

/**
 * 测试schedule和scheduleAtFixedRate的区别
 * @Author Rhine
 * @Date 2019/1/21 15:46
 **/
public class DifferenceTest {
    public static void main(String[] args) {
        //规定时间格式
        final SimpleDateFormat sf=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        //获取当前的具体时间
        Calendar calendar=Calendar.getInstance();
        System.out.println("Current time is: "+ sf.format(calendar.getTime()));
        Timer timer=new Timer();


//        timer.schedule(new TimerTask() {
//            @Override
//            public void run() {
//                try {
//                    Thread.sleep(3000);
//                } catch (InterruptedException e) {
//                    e.printStackTrace();
//                }
//                //打印当前的计划执行时间
//                System.out.println("Scheduled exec time is: "+sf.format(scheduledExecutionTime()));
//                System.out.println("Task is being executed!");
//            }
//        },calendar.getTime(),2000);


        timer.scheduleAtFixedRate(new TimerTask() {
            @Override
            public void run() {
                try {
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                //打印当前的计划执行时间
                System.out.println("Scheduled exec time is: "+sf.format(scheduledExecutionTime()));
                System.out.println("Task is being executed!");
            }
        },calendar.getTime(),2000);

    }
}

 

 

 

 

 

 

其他的重要函数:

TimerTask中:

cancel()   取消当前TimerTask里的任务,但不停止线程

package timer;

import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.TimerTask;

/**
 * @Author Rhine
 * @Date 2019/1/21 0:44
 **/
public class MyTimerTask extends TimerTask {

    private String name;

    private Integer count=0;

    public MyTimerTask(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public void run() {
        if (count<3){
            //以yyyy-MM-dd HH:mm:ss的格式打印当前执行时间
            //如2016-11-11 00:00:00
            Calendar calendar=Calendar.getInstance();
            SimpleDateFormat sf=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            System.out.println("Current exec time is: "+sf.format(calendar.getTime()));

            //打印当前name的内容
            System.out.println("Current exce name is: "+name);
            count++;
        }else {
            cancel();
            System.out.println("Task cancel!");
        }

    }
}
package timer;

import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Timer;

/**
 * @Author Rhine
 * @Date 2019/1/21 0:46
 **/
public class MyTimer1 {

    public static void main(String[] args) {

        //1.创建一个timer实例
        Timer timer=new Timer();
        //2.创建一个MyTimerTask实例
        MyTimerTask myTimerTask=new MyTimerTask("No.1");

        /**
         * 获取当前时间,并设置成距离当前时间三秒之后的时间
         * 如当前是2016-11-10 23:59:57
         * 则设置后的时间则为2016-11-11 00:00:00
         */
        Calendar calendar=Calendar.getInstance();
        SimpleDateFormat sf=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        System.out.println(sf.format(calendar.getTime()));
        calendar.add(Calendar.SECOND,3);


        /**
         *  schedule(task,delay,period)
         *  等待delay毫秒后首次执行task,之后每隔period毫秒重复执行一次task
         */
        myTimerTask.setName("schedule");
        timer.schedule(myTimerTask,3000,2000);
        
    }
}

 

scheduledExecutionTime()  返回此任务最近实际执行的已安排的执行时间,返回的long,需要SimpleDateFormat格式一下

package timer;

import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Timer;

/**
 * @Author Rhine
 * @Date 2019/1/21 0:46
 **/
public class MyTimer1 {

    public static void main(String[] args) {

        //1.创建一个timer实例
        Timer timer=new Timer();
        //2.创建一个MyTimerTask实例
        MyTimerTask myTimerTask=new MyTimerTask("No.1");

        /**
         * 获取当前时间,并设置成距离当前时间三秒之后的时间
         * 如当前是2016-11-10 23:59:57
         * 则设置后的时间则为2016-11-11 00:00:00
         */
        Calendar calendar=Calendar.getInstance();
        SimpleDateFormat sf=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        System.out.println(sf.format(calendar.getTime()));
        calendar.add(Calendar.SECOND,3);


        /**
         *  schedule(task,delay,period)
         *  等待delay毫秒后首次执行task,之后每隔period毫秒重复执行一次task
         */
        myTimerTask.setName("schedule");
        timer.schedule(myTimerTask,3000);
        System.out.println("schedule time is: "+sf.format(myTimerTask.scheduledExecutionTime()));

    }
}

 

Timer中

cancel()  终止此计时器,丢弃所有当前已安排的任务,关闭线程

package timer;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Timer;

/**
 * @Author Rhine
 * @Date 2019/1/21 12:02
 **/
public class CancelTest {

    public static void main(String[] args) throws InterruptedException {
        //创建Timer实例
        Timer timer=new Timer();
        //创建TimerTask实例
        MyTimerTask task1=new MyTimerTask("task");
        MyTimerTask task2=new MyTimerTask("task2");
        //获取当前的执行时间并打印
        Date startTime=new Date();
        SimpleDateFormat sf=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        System.out.println("start time is: "+sf.format(startTime));

        //task1首次执行是距离现在时间3秒后执行,之后每隔2秒执行一次
        //task1首次执行是距离现在时间3秒后执行,之后每隔2秒执行一次
        timer.schedule(task1,3000,2000);
        timer.schedule(task2,1000,2000);
        
        //休眠5秒
        Thread.sleep(5000);
        //获取当前的执行时间并打印
        Date cancelTime=new Date();
        System.out.println("cancel time is: "+sf.format(cancelTime));
        
        //取消所有任务
        timer.cancel();
        System.out.println("Task all canceled!");

    }
}

purge()    从此计时器队列中移除所有已取消 的任务,返回从队列中移除的任务数

package timer;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Timer;

/**
 * @Author Rhine
 * @Date 2019/1/21 12:02
 **/
public class CancelTest {

    public static void main(String[] args) throws InterruptedException {
        //创建Timer实例
        Timer timer=new Timer();
        //创建TimerTask实例
        MyTimerTask task1=new MyTimerTask("task");
        MyTimerTask task2=new MyTimerTask("task2");
        //获取当前的执行时间并打印
        Date startTime=new Date();
        SimpleDateFormat sf=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        System.out.println("start time is: "+sf.format(startTime));

        //task1首次执行是距离现在时间3秒后执行,之后每隔2秒执行一次
        //task1首次执行是距离现在时间3秒后执行,之后每隔2秒执行一次
        timer.schedule(task1,3000,2000);
        timer.schedule(task2,1000,2000);
        System.out.println("Current canceled task number is: "+timer.purge());
        //休眠5秒
        Thread.sleep(5000);
        //获取当前的执行时间并打印
        Date cancelTime=new Date();
        System.out.println("cancel time is: "+sf.format(cancelTime));

        //取消所有任务
        task2.cancel();
        System.out.println("Current canceled task number is: "+timer.purge());

    }
}

 

 

Demo:

第一次机器人会隔两秒钟打印一次计划的时间、执行内容

第二个机器人会模拟往桶里灌水,直到桶里的水满为止

Task: 

package timer;

import java.text.SimpleDateFormat;
import java.util.Timer;
import java.util.TimerTask;

/**
 * @Author Rhine
 * @Date 2019/1/21 16:16
 **/
public class DancingRobot extends TimerTask {
    private Timer timer;

    public DancingRobot(Timer timer) {
        this.timer = timer;
    }

    @Override
    public void run() {
        //获取最近的一次任务执行时间,并将其格式化
        SimpleDateFormat sf=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        System.out.println("Scheduled exec time is: "+sf.format(scheduledExecutionTime()));
        System.out.println("Dancing Happily!");
    }
}
package timer;

import java.util.Timer;
import java.util.TimerTask;

/**
 * @Author Rhine
 * @Date 2019/1/21 16:19
 **/
public class WaterRobot extends TimerTask {
    private Timer timer;

    public WaterRobot(Timer timer) {
        this.timer = timer;
    }

    //最大容量为5
    private Integer bucketCapacity=0;
    @Override
    public void run() {
        if (bucketCapacity<5){
            //灌水直至桶满为止
            System.out.println("Add 1L water into bucket!");
            bucketCapacity++;
        }else {
            //水满之后就停止执行
            System.out.println("The number of canceled task in timer is: "+timer.purge());
            cancel();
            System.out.println("The waterRobot has been aborted");
            System.out.println("The number of canceled task in timer is: "+timer.purge());
            System.out.println("Current water is "+bucketCapacity+"L");
            //等待两秒钟,终止timer里面的所有内容
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            timer.cancel();
        }
    }
}

Timer :

package timer;

import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Timer;

/**
 * @Author Rhine
 * @Date 2019/1/21 16:22
 **/
public class Executor {
    public static void main(String[] args) {
        Timer timer=new Timer();
        //获取当前的时间
        Calendar calendar=Calendar.getInstance();
        SimpleDateFormat sf=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        System.out.println("Current time is: "+sf.format(calendar.getTime()));

        DancingRobot dr=new DancingRobot(timer);
        WaterRobot wr=new WaterRobot(timer);
        timer.schedule(dr,calendar.getTime(),2000);
        timer.scheduleAtFixedRate(wr,calendar.getTime(),1000);
    }
}

 

 

 

Timer的缺陷:

1、管理并发的缺陷以及任务之间的协同缺陷(因为后台只有一个线程,一个Timer,多个任务之间也是串行运行) 

2、当任务抛出异常时的缺陷(抛出异常时,剩下的所有task都会被终止,因为单个线程运行)

所以Timer的使用禁区

1、对时效性要求比较较高的多任务并发作业    2、对复杂任务的调度

同时例如:每周三执行任务也是不支持的

一个Timer只有一个线程,多个schedule时(scheduleAtFixedRate)也是串行。

Task:

package timer;

import javax.crypto.SecretKey;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.TimerTask;

/**
 * @Author Rhine
 * @Date 2019/1/21 16:43
 **/
public class MyTimerTask3 extends TimerTask {
    private String name;
    private long costTime;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public long getCostTime() {
        return costTime;
    }

    public void setCostTime(long costTime) {
        this.costTime = costTime;
    }

    public MyTimerTask3(String name, long costTime) {
        this.name = name;
        this.costTime = costTime;
    }

    @Override
    public void run() {
        //以yyyy-MM-dd HH:mm:ss的格式打印当前执行时间
        //如2016-11-11 00:00:00
        Calendar calendar=Calendar.getInstance();
        SimpleDateFormat sf=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        System.out.println(name+"'s current exec time is: "+sf.format(calendar.getTime()));

        try {
            Thread.sleep(costTime);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        //获取costTime之后的时间
        calendar=Calendar.getInstance();
        System.out.println(name+"'s finish time is: "+sf.format(calendar.getTime()));
        throw new RuntimeException();

    }
}

 

Timer: 

package timer;

import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Timer;

/**
 * @Author Rhine
 * @Date 2019/1/21 16:50
 **/
public class MyTimer2 {
    public static void main(String[] args) {
        //创建一个timer实例
        Timer timer=new Timer();
        //创建一个MyTimerTask实例
        MyTimerTask3 myTimerTask1=new MyTimerTask3("No.1",2000);
        MyTimerTask3 myTimerTask2=new MyTimerTask3("No.2",2000);

        /**
         * 获取当前时间,并设置成距离当前时间三秒之后的时间 如当前是2016-11-10 23:59:57
         */
        Calendar calendar=Calendar.getInstance();
        SimpleDateFormat sf=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        System.out.println("current time is "+sf.format(calendar.getTime()));
//        timer.schedule(myTimerTask1,calendar.getTime());
//        timer.schedule(myTimerTask2,calendar.getTime());

        timer.scheduleAtFixedRate(myTimerTask1,calendar.getTime(),2000);
        timer.scheduleAtFixedRate(myTimerTask2,calendar.getTime(),2000);
    }
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值