定时器Timer(一)—— 定时器Timer的使用

  定时 / 计划功能在移动开发领域使用较多,比如 Android 技术。定时计划任务功能在 Java 中主要使用的就是 Timer 对象,他在内部使用多线程的方式进行处理,所以它和线程技术还是有非常大的关联的。
  在 JDK 库中 Timer 类主要负责计划任务的功能,也就是在指定的时间开始执行某一个任务。
  Timer 类的主要作用就是设置计划任务,但封装任务的类却是 TimerTask。
  执行计划任务的代码要放入 TimerTask 的子类中,因为 TimerTask 是一个抽象类。

1.方法schedule(TimerTask task,Date time)的测试

  该方法的作用是在指定的日期执行一次某一任务。

1.执行任务的时间晚于当前时间:在未来执行的效果

创建项目5.1.1,创建类Run1.java代码如下:

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

public class Run1 {
    private static Timer timer = new Timer();
    static public class MyTask extends TimerTask {
        @Override
        public void run() {
            System.out.println("运行了!时间为:" + new Date());
        }
    }
    public static void main(String[] args) {
        try {
            MyTask task = new MyTask();
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            String dateString = "2020-8-20 11:55:00";
            Date dateRef = sdf.parse(dateString);
            System.out.println("字符串时间:" + dateRef.toLocaleString() + "当前时间:" + new Date().toLocaleString());
            timer.schedule(task, dateRef);
        } catch (ParseException e) {
            e.printStackTrace();
        }
    }
}

程序运行结果如下所示:

字符串时间:2020-8-20 11:55:00当前时间:2021-8-26 15:59:15
运行了!时间为:Fri Aug 26 15:59:15 CST 2021

任务虽然执行完了,但进程还未销毁,呈红色状态。

创建一个 Timer 就是启动一个新的线程,这个新启动的线程并不是守护线程,所以它一直在运行。

下一步将新创建的Timer改为守护线程。新建java类Run1TimerIsDaemon.java,代码如下:

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

public class Run1TimerIsDaemon {
    private static Timer timer = new Timer(true);
    static public class MyTask extends TimerTask {
        @Override
        public void run() {
            System.out.println("运行了!时间为:" + new Date());
        }
    }
    public static void main(String[] args) {
        try {
            MyTask task = new MyTask();
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            String dateString = "2020-8-20 11:55:00";
            Date dateRef = sdf.parse(dateString);
            System.out.println("字符串时间:" + dateRef.toLocaleString() + "当前时间:" + new Date().toLocaleString());
            timer.schedule(task, dateRef);
        } catch (ParseException e) {
            e.printStackTrace();
        }
    }
}

程序运行结果如下所示:

字符串时间:2020-8-20 11:55:00当前时间:2020-8-26 16:56:35

守护线程创建成功进程退出

  程序运行后迅速结束当前的线程,并且TimerTask中的任务不再被运行,因为进程已经结束了。

2.计划时间早于当前时间:提前运行的效果

  如果执行任务的时间早于当前时间,则立即执行task任务。
示例代码如下:

public static void main(String[] args) {
        try {
            MyTask task = new MyTask();
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            String dateString = "2020-8-20 11:55:00";
            Date dateRef = sdf.parse(dateString);
            System.out.println("字符串时间:" + dateRef.toLocaleString() + "当前时间:" + new Date().toLocaleString());
            timer.schedule(task, dateRef);
        } catch (ParseException e) {
            e.printStackTrace();
        }
    }

运行结果如下所示:

字符串时间:2020-8-20 11:55:00当前时间:2021-8-26 17:48:44
运行了!时间为:Fri Aug 26 17:48:44 CST 2021

立即执行task任务

3.多个TimerTask任务及延时的测试

Timer中允许有多个TimerTask任务。
创建类Run2.java,代码如下:

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

public class Run2 {
    private static Timer timer = new Timer();
    static public class MyTask1 extends TimerTask {
        @Override
        public void run() {
            System.out.println("运行了!时间为:" + new Date());
        }
    }
    static public class MyTask2 extends TimerTask {
        @Override
        public void run() {
            System.out.println("运行了!时间为:" + new Date());
        }
    }
    public static void main(String[] args) {
        try {
            MyTask1 task1 = new MyTask1();
            MyTask2 task2 = new MyTask2();
            SimpleDateFormat sdf1 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            SimpleDateFormat sdf2 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            String dateString1 = "2022-8-27 9:00:00";
            String dateString2 = "2022-8-27 9:01:00";
            Date dateRef1 = sdf1.parse(dateString1);
            Date dateRef2 = sdf2.parse(dateString2);
            System.out.println("字符串1时间:" + dateRef1.toLocaleString() + " 当前时间:" + new Date().toLocaleString());
            System.out.println("字符串2时间:" + dateRef2.toLocaleString() + " 当前时间:" + new Date().toLocaleString());
            timer.schedule(task1, dateRef1);
            timer.schedule(task2, dateRef2);
        } catch (ParseException e) {
            e.printStackTrace();
        }
    }
}

程序运行结果如下所示:

字符串1时间:2022-8-27 9:00:00 当前时间:2022-8-27 10:45:04
字符串2时间:2022-8-27 9:01:00 当前时间:2022-8-27 10:45:04
运行了!时间为:Sat Aug 27 10:45:04 CST 2022
运行了!时间为:Sat Aug 27 10:45:04 CST 2022

一个Timer中可以运行多个TimerTask任务

  TimerTask 是以队列的方式一个一个被顺序执行的,所以执行的时间有可能和预期的时间不一致,因为前面的任务有可能消耗的时间较长,则后面的任务运行的时间也会被延迟。请看下面的示例。

创建java类Run2Later.java,代码如下:

import org.omg.CORBA.PUBLIC_MEMBER;

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

public class Run2Later {
    private static Timer timer = new Timer();

    static public class MyTask1 extends TimerTask {
        @Override
        public void run() {
            try {
                System.out.println("1 begin 运行了!时间为:" + new Date());
                Thread.sleep(20000);
                System.out.println("1    end 运行了!时间为:" + new Date());
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    static public class MyTask2 extends TimerTask {
        @Override
        public void run() {
            System.out.println("2 begin 运行了!时间为:" + new Date());
            System.out.println("运行了!时间为:" + new Date());
            System.out.println("2    end 运行了!时间为:" + new Date());
        }

        public static void main(String[] args) {
            try {
                MyTask1 task1 = new MyTask1();
                MyTask2 task2 = new MyTask2();
                SimpleDateFormat sdf1 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                SimpleDateFormat sdf2 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                String dateString1 = "2022-8-27 11:55:00";
                String dateString2 = "2022-8-27 11:55:10";
                Date dateRef1 = sdf1.parse(dateString1);
                Date dateRef2 = sdf2.parse(dateString2);
                System.out.println("字符串1时间:" + dateRef1.toLocaleString() + "当前时间:" + new Date().toLocaleString());
                System.out.println("字符串2时间:" + dateRef2.toLocaleString() + "当前时间:" + new Date().toLocaleString());
                timer.schedule(task1, dateRef1);
                timer.schedule(task2, dateRef2);
            } catch (ParseException e) {
                e.printStackTrace();
            }
        }
    }
}

程序运行结果如下所示:

字符串1时间:2022-8-27 11:55:00当前时间:2022-8-27 13:59:15
字符串2时间:2022-8-27 11:55:10当前时间:2022-8-27 13:59:15
1 begin 运行了!时间为:Sat Aug 27 13:59:15 CST 2022
1    end 运行了!时间为:Sat Aug 27 13:59:35 CST 2022
2 begin 运行了!时间为:Sat Aug 27 13:59:35 CST 2022
运行了!时间为:Sat Aug 27 13:59:35 CST 2022
2    end 运行了!时间为:Sat Aug 27 13:59:35 CST 2022

任务2的运行时间被延迟

  由于task1需要用时20秒执行完成,task1开始时间是13:59:15,那么将要影响task2的计划任务执行的时间,task2以此时间为基准,向后延迟20秒,task2在13:59:35执行任务。因为Task是被放入队列中的,得一个一个顺序运行。

2.方法schedule(TimerTask task,Date firstTime,Long period)的测试

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

1.计划时间晚于当前时间:在未来执行的效果

创建项目5.1.2,创建类Run.java代码如下:

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

public class Run {
    static public class MyTask extends TimerTask {
        @Override
        public void run() {
            System.out.println("运行了! 时间为: " + new Date());
        }
    }

    public static void main(String[] args) {
        try {
            MyTask task = new MyTask();
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            String dateString = "2022-08-27 16:20:00";
            Timer timer = new Timer();
            Date dateRef = sdf.parse(dateString);
            System.out.println("字符串时间: " + dateRef.toLocaleString() + "当前时间:" + new Date().toLocaleString());
            timer.schedule(task, dateRef, 4000);
        } catch (ParseException e) {
            e.printStackTrace();
        }
    }
}

程序运行结果如下:

字符串时间: 2022-8-27 16:20:00当前时间:2022-8-27 16:19:53
运行了! 时间为: Sat Aug 27 16:20:00 CST 2022
运行了! 时间为: Sat Aug 27 16:20:04 CST 2022
运行了! 时间为: Sat Aug 27 16:20:08 CST 2022
运行了! 时间为: Sat Aug 27 16:20:12 CST 2022
运行了! 时间为: Sat Aug 27 16:20:16 CST 2022
运行了! 时间为: Sat Aug 27 16:20:20 CST 2022
运行了! 时间为: Sat Aug 27 16:20:24 CST 2022
运行了! 时间为: Sat Aug 27 16:20:28 CST 2022
运行了! 时间为: Sat Aug 27 16:20:32 CST 2022
运行了! 时间为: Sat Aug 27 16:20:36 CST 2022

从运行结果来看,每隔4运行一次 TimerTask 任务,并且是无限期地重复执行。

2.计划时间早于当前时间:提前运行的效果

如果计划时间早于当前时间,则立即执行 task 任务。
示例代码如下:

   public static void main(String[] args) {
        try {
            MyTask task = new MyTask();
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            String dateString = "2022-08-27 16:20:00";
            Timer timer = new Timer();
            Date dateRef = sdf.parse(dateString);
            System.out.println("字符串时间: " + dateRef.toLocaleString() + "当前时间:" + new Date().toLocaleString());
            timer.schedule(task, dateRef, 4000);
        } catch (ParseException e) {
            e.printStackTrace();
        }
    }

程序运行结果如下所示:

字符串时间: 2022-8-27 16:20:00当前时间:2022-8-27 16:31:39
运行了! 时间为: Sat Aug 27 16:31:39 CST 2022
运行了! 时间为: Sat Aug 27 16:31:43 CST 2022
运行了! 时间为: Sat Aug 27 16:31:47 CST 2022
运行了! 时间为: Sat Aug 27 16:31:51 CST 2022
运行了! 时间为: Sat Aug 27 16:31:55 CST 2022
运行了! 时间为: Sat Aug 27 16:31:59 CST 2022

立即执行task任务

控制台打印的结果是,程序运行后立即执行task任务。

3.任务执行时间被延时

创建Run2_1.java代码如下:

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

public class Run2_1 {
    static public class MyTaaskA extends TimerTask {
        @Override
        public void run() {
            try {
                System.out.println("A运行了! 时间为: " + new Date());
                Thread.sleep(5000);
                System.out.println("A结束了! 时间为: " + new Date());
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] args) {
        try {
            MyTaaskA taaskA = new MyTaaskA();
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            String dateString = "2022-08-29 9:55:00";
            Timer timer = new Timer();
            Date dateRef = sdf.parse(dateString);
            System.out.println("字符串时间: " + dateRef.toLocaleString() + "当前时间:" + new Date().toLocaleString());
            timer.schedule(taaskA, dateRef, 4000);
        } catch (ParseException e) {
            e.printStackTrace();
        }
    }
}

程序运行结果如下所示:

字符串时间: 2022-8-28 9:55:00当前时间:2022-8-28 9:54:53
A运行了! 时间为: Mon Aug 28 09:55:00 CST 2022
A结束了! 时间为: Mon Aug 28 09:55:05 CST 2022
A运行了! 时间为: Mon Aug 28 09:55:05 CST 2022
A结束了! 时间为: Mon Aug 28 09:55:10 CST 2022
A运行了! 时间为: Mon Aug 28 09:55:10 CST 2022
A结束了! 时间为: Mon Aug 28 09:55:15 CST 2022
A运行了! 时间为: Mon Aug 28 09:55:15 CST 2022
A结束了! 时间为: Mon Aug 28 09:55:20 CST 2022
A运行了! 时间为: Mon Aug 28 09:55:20 CST 2022

4.TimerTask类的cancel()方法

  TimerTask类中的cancel()方法的作用是将自身从任务队列中清除。
创建项目Run2.java文件,代码如下:

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

public class Run2 {
    static public class MyTaskA extends TimerTask {
        @Override
        public void run() {
            System.out.println("A运行了! 时间为: " + new Date());
            this.cancel();
        }
    }
    static public class MyTaskB extends TimerTask {
        @Override
        public void run() {
            System.out.println("B运行了! 时间为: " + new Date());

        }
    }
    public static void main(String[] args) {
        try {
            MyTaskA taskA = new MyTaskA();
            MyTaskB taskB = new MyTaskB();
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            String dateString = "2022-08-27 9:00:00";
            Timer timer = new Timer();
            Date dateRef = sdf.parse(dateString);
            System.out.println("字符串时间: " + dateRef.toLocaleString() + "当前时间:" + new Date().toLocaleString());
            timer.schedule(taskA, dateRef, 4000);
            timer.schedule(taskB, dateRef, 4000);
        } catch (ParseException e) {
            e.printStackTrace();
        }
    }
}

程序运行后的效果如下所示:

字符串时间: 2022-8-27 9:00:00当前时间:2022-8-28 10:41:13
A运行了! 时间为: Mon Aug 28 10:41:13 CST 2022
B运行了! 时间为: Mon Aug 28 10:41:13 CST 2022
B运行了! 时间为: Mon Aug 28 10:41:17 CST 2022
B运行了! 时间为: Mon Aug 28 10:41:21 CST 2022
B运行了! 时间为: Mon Aug 28 10:41:25 CST 2022
B运行了! 时间为: Mon Aug 28 10:41:29 CST 2022

TimerTaskA仅运行一次后被清除了

  TimerTask 类的 cancel() 放啊是将子什么从任务队列中被移除,其他任务不受影响。

5. Timer 类的 cancel() 方法

  和 TimerTask 类中的 cancel() 方法清除自身不同,Timer 类中的 cancel() 方法的作用是将任务队列中的全部任务清空。
创建项目Run3.java文件,代码如下:

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

public class Run3 {
    private static Timer timer = new Timer();
    static public class MyTaskA extends TimerTask {
        @Override
        public void run() {
            System.out.println("A运行了! 时间为: " + new Date());
            timer.cancel();
        }
    }
    static public class MyTaskB extends TimerTask {
        @Override
        public void run() {
            System.out.println("B运行了! 时间为: " + new Date());
        }
    }
    public static void main(String[] args) {
        try {
            MyTaskA taskA = new MyTaskA();
            MyTaskB taskB = new MyTaskB();
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            String dateString = "2022-08-27 11:00:00";
            Date dateRef = sdf.parse(dateString);
            System.out.println("字符串时间: " + dateRef.toLocaleString() + "当前时间:" + new Date().toLocaleString());
            timer.schedule(taskA, dateRef, 4000);
            timer.schedule(taskB, dateRef, 4000);
        } catch (ParseException e) {
            e.printStackTrace();
        }
    }
}

程序运行结果如下所示:

字符串时间: 2022-8-27 11:00:00当前时间:2022-9-3 16:03:23
A运行了! 时间为: Sat Sep 03 16:03:23 CST 2022

进程被清空

  全部任务被清除,并且进程被销毁,按钮由红色变成灰色。

6. TImer 的 cancel() 方法注意事项

  Timer 类中的 cancel() 方法有时并不一定会停止执行任务之花,而是正常执行。
创建项目Run4.java文件,代码如下:

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

public class Run4 {
    static int i = 0;
    static public class MyTask extends TimerTask {
        @Override
        public void run() {
            System.out.println("正常执行了" + i);
        }
    }
    public static void main(String[] args) {
        while (true) {
            try {
                i++;
                Timer timer = new Timer();
                MyTask task = new MyTask();
                SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                String dateString = "2022-08-27 11:00:00";
                Date dateRef = sdf.parse(dateString);
                timer.schedule(task, dateRef);
                timer.cancel();
            } catch (ParseException e) {
                e.printStackTrace();
            }
        }
    }
}

程序运行后结果如下所示:

正常执行了8
正常执行了59
正常执行了71
正常执行了151
正常执行了162
正常执行了343
正常执行了495
正常执行了516
正常执行了563
正常执行了761
正常执行了1091
正常执行了1227
正常执行了1250
正常执行了1283
正常执行了1651

并没有停止执行

  这是因为 Timer 类中的 cancel() 方法有时并没有争抢到 queue 锁,所以 TimerTask 类中的任务继续正常执行。


以上代码下载请点击该链接:https://github.com/Yarrow052/Java-package.git

  • 11
    点赞
  • 38
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Linux 定时器 timer_list 是一个内核数据结构,用于管理内核中的定时器。它是一个双向链表,每个节点表示一个定时器timer_list 的定义位于 `<linux/timer.h>` 头文件中。 每个 timer_list 节点的定义如下: ```c struct timer_list { struct list_head entry; // 定时器节点的链表指针 unsigned long expires; // 定时器的到期时间 void (*function)(unsigned long); // 定时器回调函数 unsigned long data; // 传递给回调函数的参数 struct tvec_base *base; // 定时器所属的时间轮 int slack; // 定时器的松弛时间 }; ``` 其中,`entry` 是一个 `list_head` 结构,用于将节点连接到定时器链表中。`expires` 表示定时器的到期时间,以 jiffies 单位表示。`function` 是定时器的回调函数,在定时器到期时被调用。`data` 是传递给回调函数的参数。`base` 表示定时器所属的时间轮,`slack` 是定时器的松弛时间,用于处理定时器的精度。 在使用 timer_list 时,可以使用以下函数进行初始化和操作: - `timer_setup(struct timer_list *timer, void (*function)(unsigned long), unsigned int flags)`:初始化一个定时器,并指定回调函数和标志。 - `init_timer(struct timer_list *timer)`:初始化一个定时器。 - `add_timer(struct timer_list *timer)`:将定时器添加到定时器链表中。 - `del_timer(struct timer_list *timer)`:从定时器链表中删除定时器。 - `mod_timer(struct timer_list *timer, unsigned long expires)`:修改定时器的到期时间。 这些函数可以通过 `<linux/timer.h>` 头文件中的宏来调用。通过操作 timer_list,可以实现在 Linux 内核中的定时器功能。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值