1

时间:2017年05月24日星期三
说明:本文部分内容均来自慕课网。@慕课网:http://www.imooc.com
教学示例源码:无
个人学习源码:https://github.com/zccodere/s...

第一章:课程介绍

1-1 课程介绍

什么是定时任务调度

基于给定的时间点,给定的时间间隔或者给定的执行次数自动执行的任务

在Java中的定时调度工具

Timer:小弟,能实现日常60%的定时任务
Quartz:大哥,能搞定一切

Timer与Quartz区别

出身不同
    Timer由JDK提供,调用方式简单粗暴,不需要别的jar包支持
    Quartz源于开源项目,非JDK自带,需要引入别的jar包支持
能力区别:主要体现在对时间的控制上
    Timer实现如某个具体时间执行什么任务
    Quartz实现如每个星期天8点执行什么任务
    Quartz的时间控制功能远比Timer强大和完善
底层机制
    Timer走后台线程执行定时任务
    Quartz能够使用多个执行线程去执行定时任务
    Quartz确实比Timer强大很多,但是,Timer能实现的功能,尽量不动用Quartz
        毕竟大哥的出场费要比小弟高

前置知识

clipboard.png

第二章:Timer简介

2-1 Timer简介

本节内容

Timer纸上谈兵:Timer的定义以及架构
Timer实战演练:通过一个实际的例子,让大家对Timer有一个初步的了解

Timer的定义

有且仅有一个后台线程对多个业务线程进行定时定频率的调度

主要构件

clipboard.png

Timer工具类详解

clipboard.png

通过程序来讲解Timer

代码演示

1.编写需定时调度的业务逻辑类

package com.myimooc.timer;

import java.util.TimerTask;

/**
 * 需定时调度的业务逻辑类
 * Created by ChangComputer on 2017/5/24.
 */
// 继承 TimerTask 类
public class MyTimerTask extends TimerTask{
    private String name;

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

    // 重写 run 方法
    @Override
    public void run() {

        // 打印当前 name 的内容
        System.out.println("Current exec name is : " + name);

    }

    public String getName() {
        return name;
    }

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

2.编写定时调度类

package com.myimooc.timer;

import java.util.Timer;

/**
 * 定时调度类
 * Created by ChangComputer on 2017/5/24.
 */
public class MyTimer {

    public static void main(String[] args){

        // 创建一个 Timer 实例
        Timer timer = new Timer();

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

        // 通过 Timer 定时定频率调用 MyTimerTask 的业务逻辑
        // 即第一次执行是在当前时间的两秒之后,之后每隔一秒钟执行一次
        timer.schedule(myTimerTask,2000L,1000L);
    }
}

第三章:Timer函数和综合应用

3-1 Timer定时函数的用法

本节内容

schedule的四种用法
scheduleAtFixedRate的两种用法

第一种用法:schdule(task, time)

参数
    task:所要安排的任务
    time:执行任务的时间
作用
    在时间等于或者超过time的时候执行且仅执行一次task

第二种用法:schedule(task, time, period)

参数
    task:所要安排的任务
    time:首次执行任务的时间
    period:执行一次task的时间间隔,单位是毫秒
作用
    时间等于或者超过time时首次执行task
    之后每隔period毫秒重复执行一次task

第三种用法:schedule(task, delay)

参数
    task:所要安排的任务
    delay:执行任务前的延迟时间,单位是毫秒
作用
    等待delay毫秒后仅执行且执行一个task

第四种用法:schedule(task, delay, period)

参数
    task:所要安排的任务
    delay:执行任务前的延迟时间,单位是毫秒
    period:执行一次task的时间间隔,单位是毫秒
作用
    等到delay毫秒后首次执行task
    之后每隔period毫秒重复执行一次task

第一种用法:scheduleAtFixedRate(task, time, period)

参数
    task:所要安排的任务
    time:首次执行任务的时间
    period:执行一次task的时间间隔,单位是毫秒
作用
    时间等于或超过time时首次执行task
    之后每隔period毫秒重复执行一次task

第二种用法:scheduleAtFixedRate(task, delay, period)

参数
    task:所要安排的任务
    delay:执行任务前的延迟时间,单位是毫秒
    period:执行一次task的时间间隔,单位是毫秒
作用
    等到delay毫秒后首次执行task
    之后每隔period毫秒重复执行一次task

代码演示

1.修改需定时调度的业务逻辑类

/**
 * 需定时调度的业务逻辑类
 * Created by ChangComputer on 2017/5/24.
 */
// 继承 TimerTask 类
public class MyTimerTask extends TimerTask{
    private String name;

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

    // 重写 run 方法
    @Override
    public void run() {
        // 以yyyy-MM-dd HH:mm:ss的格式打印当前执行时间
        // 如2016-11-11 00:00:00
        Calendar calendar = Calendar.getInstance();
        // 定义日期格式
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        System.out.println("Current exec time is : " + simpleDateFormat.format(calendar.getTime()));

        // 打印当前 name 的内容
        System.out.println("Current exec name is : " + name);

    }

    public String getName() {
        return name;
    }

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

2.修改定时调度类

package com.myimooc.timer;

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

/**
 * 定时调度类
 * Created by ChangComputer on 2017/5/24.
 */
public class MyTimer {

    public static void main(String[] args){

        // 创建一个 Timer 实例
        Timer timer = new Timer();

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

        // 通过 Timer 定时定频率调用 MyTimerTask 的业务逻辑
        // 即第一次执行是在当前时间的两秒之后,之后每隔一秒钟执行一次
        //timer.schedule(myTimerTask,2000L,1000L);

        /**
         * 获取当前时间,并设置成距离当前时间三秒之后的时间
         * 如当前时间是2016-11-10 23:59:57
         * 则设置后的时间则为2016-11-11 00:00:00
         */
        Calendar calendar = Calendar.getInstance();
        // 定义日期格式
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        System.out.println("Current exec time is : " + simpleDateFormat.format(calendar.getTime()));

        calendar.add(Calendar.SECOND,3);

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

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

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

        /**
         * 4.等到delay毫秒后首次执行task
         *   之后每隔period毫秒重复执行一次task
         *   如现在是2016-11-11 00:00:00
         *   则在2016-11-11 00:00:01第一次执行task,打印任务名字
         *   之后每隔两秒执行一次task
         */
        //myTimerTask.setName("schedule4");
        //timer.schedule(myTimerTask,1000L,2000L);

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

        /**
         * 2.等待delay毫秒后首次执行task
         *   之后每隔period毫秒重复执行一次task
         *   如现在是2016-11-11 00:00:00
         *   则在2016-11-11 00:00:01第一次执行task,打印任务名字
         *   之后每隔两秒执行一次task
         */
        myTimerTask.setName("scheduleAtFixedRate2");
        timer.scheduleAtFixedRate(myTimerTask,1000L,2000L);
    }
}

3-2 其他重要函数

本节内容

TimerTask的cancel(), scheduleExecutionTime()
Timer的cancel(), purge()

TimerTask类的cancel()

作用
    取消当前TimerTask里的任务

TimerTask类的scheduleExecutionTime()

作用
    返回此任务最近实际执行的已安排执行的时间
返回值
    最近发生此任务执行安排的时间,为long型

Timer的cancel()

作用
    终止此计时器,丢弃所有当前已安排的任务

Timer的purge()

作用
    从此计时器的任务队列中移除所有已取消的任务
返回值
    从队列中移除的任务数

代码演示

package com.myimooc.timer;

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

/**
 * Created by ChangComputer on 2017/5/24.
 */
public class CancelTest {

    public static void main(String[] args) throws InterruptedException {
        // 创建Timer实例
        Timer timer = new Timer();
        // 创建TimerTask实例
        MyTimerTask task1 = new MyTimerTask("task1");
        MyTimerTask task2 = new MyTimerTask("task2");
        // 获取当前的执行时间并打印
        Date startTime = new Date();
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        System.out.println("start time is : " + simpleDateFormat.format(startTime));
        // task1首次执行是距离现在时间3秒后执行,之后每隔2秒执行一次
        // task1首次执行是距离现在时间1秒后执行,之后每隔2秒执行一次
        timer.schedule(task1,3000L,2000L);
        timer.schedule(task2,1000L,2000L);
        System.out.println("current canceled task number is : " + timer.purge());
        // 休眠5秒
        Thread.sleep(2000L);
        // 获取当前的执行时间并打印
        Date cancelTime = new Date();
        System.out.println("cancel time is : " + simpleDateFormat.format(cancelTime));

        // 取消所有任务
        // timer.cancel();

        task2.cancel();
        //System.out.println("Tasks all canceled!");

        System.out.println("current canceled task number is : " + timer.purge());
    }

}

3-3 schedule和scheduleAtFixedRate的区别

两种情况看区别

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

首次计划执行的时间早于当前的时间

1.schedule方法
“fixed-delay”,如果第一次执行时间被delay了,
随后的执行时间按照上一次实际执行完成的时间点进行计算
2.scheduleAtFixedRate方法
“fixed-rate”,如果第一次执行时间被delay了,
随后的执行时间按照上一次开始的时间点进行计算,
并且为了赶上进度会多次执行任务,因此TimerTask中的执行体需要考虑同步

代码演示

package com.myimooc.timer;

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

/**
 * schedule和scheduleAtFixedRate的区别 测试类
 * Created by ChangComputer on 2017/5/24.
 */
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() {
        timer.scheduleAtFixedRate(new TimerTask() {
            @Override
            public void run() {
                // 打印当前的计划执行时间
                System.out.println("Schedule exec time is : " + sf.format(scheduledExecutionTime()));
                System.out.println("Task is being executed!");
            }
        }, calendar.getTime(), 2000);

    }

}

任务执行所需时间超出任务的执行周期间隔

1. schedule方法
下一次执行时间相对于上一次实际执行完成的时间点,因此执行时间会不断延后
2.scheduleAtFixedRate方法
下一次执行时间相对于上一次开始的时间点,因此执行时间一般不会延后,因此存在并发性

代码演示

package com.myimooc.timer;

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

/**
 * schedule和scheduleAtFixedRate的区别 测试类
 * Created by ChangComputer on 2017/5/24.
 */
public class DifferenceTwoTest {

    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() {
        timer.scheduleAtFixedRate(new TimerTask() {
            @Override
            public void run() {
                try {
                    Thread.sleep(3000L);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                // 打印最近一次的计划执行时间
                System.out.println("Schedule exec time is : " + sf.format(scheduledExecutionTime()));
                System.out.println("Task is being executed!");
            }
        }, calendar.getTime(), 2000);
    }

}

3-4 Timer函数的综合应用

主要内容

通过模拟两个机器人的定时行为来把前面所学的主要函数给结合起来,让大家加深对这些函数的理解

实现两个机器人

第一个机器人会隔两秒打印最近一次计划的时间、执行内容
第二个机器人会模拟往桶里倒水,直到桶里的水满为止

灌水机器人的执行流程

clipboard.png

跳舞机器人的执行流程

图片描述

代码演示

1.创建跳舞机器人

package com.myimooc.timer;

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

/**
 * 跳舞机器人
 * Created by ChangComputer on 2017/5/24.
 */
public class DancingRobot extends TimerTask{

    /**
     * 任务执行
     * */
    @Override
    public void run() {

        // 获取最近的一次任务执行的时间并将其格式化
        SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        System.out.print("Schduled exec time is : " + sf.format(scheduledExecutionTime()));

        System.out.println("Dancing happily!");

    }
}

2.创建灌水机器人

package com.myimooc.timer;

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

/**
 * 灌水机器人
 * Created by ChangComputer on 2017/5/24.
 */
public class WaterRobot extends TimerTask{

    private Timer timer;

    // 桶容量,最大容量为5
    private Integer bucketCapacity = 0;

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

    @Override
    public void run() {
        // 灌水直至桶满为止
        if (this.bucketCapacity < 5) {
            System.out.println("Add 1L water into the 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);
            // 等待两秒钟,终止timer里面的所有内容
            try {
                Thread.sleep(2000L);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            timer.cancel();
        }

    }
}

3.创建执行类

package com.myimooc.timer;

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

/**
 * 机器人执行类
 * Created by ChangComputer on 2017/5/24.
 */
public class RobotExecutor {

    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 dancingRobot = new DancingRobot();
        WaterRobot waterRobot = new WaterRobot(timer);

        timer.schedule(dancingRobot,calendar.getTime(),2000L);
        timer.scheduleAtFixedRate(waterRobot,calendar.getTime(),1000L);




    }

}

第四章:Timer的缺陷

4-1 Timer的缺陷

Timer天生的两种缺陷
管理并发任务的缺陷

Timer有且仅有一个线程去执行定时任务,如果存在多个任务,且任务时间过长,会导致执行效果与预期不符

当任务抛出异常时的缺陷

如果TimerTask抛出RuntimeException,Timer会停止所有任务的运行

Timer的使用禁区

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

妙手空空
1.3k 声望368 粉丝

博观而约取,厚积而薄发