Java多线程-----定时器(Timer)及其实现

目录

一.定时器简介:

二.定时器的构造方法与常见方法:

三.定时器的模拟实现:

思路分析:

代码实现:


在开发中,我们经常需要一些周期性的操作,例如每隔几分钟就进行某一项操作,这时候我们就需要去设置个定时器,Java中最方便,最高效的实现方式是用java.util.Timer工具类,在通过调度java.util.TimerTask任务来完成

一.定时器简介:

①.Timer是一种工具,线程用其安排以后在后台线程中执行的任务。可安排任务执行一次,或者是定期的重复执行,实际上是个线程,定时调度所拥有的TimerTask任务

②.TimerTask是一个抽象类,它的子类由Timer安排为一次执行或者重复执行的任务,实际上就是一个拥有run方法的类,需要定时执行的代码放到run方法体内.

二.定时器的构造方法与常见方法:

①.构造方法:

②.常用方法:

注意事项:

①.上述方法中TimerTask是一个抽象方法,其子类是一个可以被Timer执行的任务,要执行的代码放在run()方法体内实现

②.schedule()与scheduleAtFixedRate()的区别? 首先schedule(TimerTask task,Date time)与schedule(TimerTask task,long delay)都只是单次执行操作,并不存在多次调用任务的情况,所以没有提供scheduleAtFixedRate方法的调用方式。它们实现的功能都一样,那区别在哪里呢? (1)schedule()方法更注重保持间隔时间的稳定:保障每隔period时间可调用一次。

(2)scheduleAtFixedRate()方法更注重保持执行频率的稳定:保障多次调用的频率趋近于period 时间,如果某一次调用时间大于period,下一次就会尽量小于period,以保障频率接近于period。

③**.每一个Timer仅对应一个线程,而不是每调用一次schedule就创建一个线程**

④.Timer是线程安全的

代码实例1:

java 复制代码
import java.util.Timer;
import java.util.TimerTask;
public class Mian {
    public static void main(String[] args) throws InterruptedException {
        //创建定时器线程对象同时设置名字
        Timer timer = new Timer("定时器线程");
        //传入要执行的任务,同时设置等待的时间
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                String current = Thread.currentThread().getName();
                System.out.println(current + " : 任务代码的执行区域~~~");
            }
        },2000);

       Thread.sleep(3000);
        System.out.println("执行结束");
        //结束定时器线程
        timer.cancel();
    }
}

运行结果:

代码实例2:

java 复制代码
import java.util.Timer;
import java.util.TimerTask;
public class Mian2 {
    public static void main(String[] args) throws InterruptedException {
        //创建定时器线程对象同时设置名字
        Timer timer = new Timer("定时器线程");
        //传入要执行的任务,同时设置等待的时间
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                String current = Thread.currentThread().getName();
                System.out.println(current + " : 任务代码的执行区域~~~");
            }
        },1000,2000);

        Thread.sleep(6000);
        //结束定时器线程
        timer.cancel();
    }
}

运行结果:

三.定时器的模拟实现:

在了解了什么是定时器和定时器的使用之后,那么定时器是如何实现的呢?这里我们通过模拟实现定时器,来进一步加深对定时器的理解。注:这里我们仅仅模拟实现Timer类不带参数的构造方法和等待delay时间后要执行的任务类,以及核心方法schedule。

思路分析:

Timer类通过schedule添加等待delay时间后执行的代码,此时的任务可能不止一个,我们需要一个容器来存放这些任务,同时,为了公平起见,我们让先到达指定时间的任务优先执行,很自然的我们可以想到用优先级队列来存储这些任务,队首元素就是最先执行的任务.

同时,我们也需要一个线程来扫描队首元素,判断队首元素是否是需要执行的任务

综上,我们自己模拟实现的定时器需要完成以下任务:

①.用一个优先级队列存放要执行的任务,队首元素是最先执行的任务

②.任务中带有时间属性,记录任务所要执行的时间

③.用一个线程来扫描队首元素,判断队首元素是否需要执行

④.这里出现多个线程同时操作共享数据的代码,我们要解决线程安全问题

代码实现:

java 复制代码
import java.util.*;
class MyTimerTask implements Comparable<MyTimerTask>{
    //要执行的任务代码
    private Runnable runnable;
    //ms级别的时间戳
    private long time;

    public MyTimerTask(Runnable runnable,long delay){
        this.runnable = runnable;
        //计算要执行的相对时间:当前时间+等待时间
        this.time = System.currentTimeMillis() + delay;
    }

    public void run(){
        runnable.run();
    }

    public long getTime(){
        return time;
    }
    //重写compareTo比较方法,按照时间的从小到大排序
    @Override
    public int compareTo(MyTimerTask o) {
        return (int)(this.time - o.time);
    }

}
//模拟实现定时器
class MyTimer{
    private PriorityQueue<MyTimerTask> q = new PriorityQueue<>();
    //锁对象
    private static Object loker = new Object();
    //构造方法中启动线程,让线程进行判定与执行
    public MyTimer(){
        Thread t = new Thread(()->{
           try{
               while(true){
                   //将操作共享数据的队列锁起来,一次只允许一个线程进行操作,避免线程安全问题
                   synchronized (loker){
                       if(q.isEmpty()){
                               loker.wait();
                       }
                       MyTimerTask current = q.peek();
                       //如果当前时间超过(>=) 设定的时间,此时需要执行任务
                       if(System.currentTimeMillis() >= current.getTime()){
                           current.run();
                           //执行完成后,将任务从队列中删除
                           q.poll();
                       }else{
                           //否则不执行任务
                           loker.wait(current.getTime() - System.currentTimeMillis());
                       }
                   }
               }
           }catch (InterruptedException e) {
               e.printStackTrace();
           }
        });
        //开启线程
        t.start();
    }

    public void schedule(Runnable runnable,long delay){
        //将操作共享数据的队列锁起来,一次只允许一个线程进行操作,避免线程安全问题
       synchronized (loker){
           MyTimerTask myTimerTask = new MyTimerTask(runnable,delay);
           q.offer(myTimerTask);
           loker.notify();
       }
    }
}
//测试
public class Demo {
    public static void main(String[] args) {
        MyTimer myTimer = new MyTimer();
        myTimer.schedule(()->{
            System.out.println("hello Thread" + ",3000  " + Thread.currentThread().getName());
        },3000);

        myTimer.schedule(()->{
            System.out.println("hello Thread" + ",2000  " + Thread.currentThread().getName());
        },2000);

        myTimer.schedule(()->{
            System.out.println("hello Thread" + ",1000  " + Thread.currentThread().getName());
        },1000);
    }
}

运行结果:

参考资料:

Java定时器的使用(Timer简介)_51CTO博客_java定时器

资源--timer的使用 - 牛李 - 博客园 (cnblogs.com)

结语: 写博客不仅仅是为了分享学习经历,同时这也有利于我巩固知识点,总结该知识点,由于作者水平有限,对文章有任何问题的还请指出,接受大家的批评,让我改进。同时也希望读者们不吝啬你们的点赞+收藏+关注,你们的鼓励是我创作的最大动力!

相关推荐
m0_571957581 小时前
Java | Leetcode Java题解之第543题二叉树的直径
java·leetcode·题解
魔道不误砍柴功3 小时前
Java 中如何巧妙应用 Function 让方法复用性更强
java·开发语言·python
NiNg_1_2343 小时前
SpringBoot整合SpringSecurity实现密码加密解密、登录认证退出功能
java·spring boot·后端
闲晨3 小时前
C++ 继承:代码传承的魔法棒,开启奇幻编程之旅
java·c语言·开发语言·c++·经验分享
测开小菜鸟5 小时前
使用python向钉钉群聊发送消息
java·python·钉钉
P.H. Infinity6 小时前
【RabbitMQ】04-发送者可靠性
java·rabbitmq·java-rabbitmq
生命几十年3万天6 小时前
java的threadlocal为何内存泄漏
java
caridle6 小时前
教程:使用 InterBase Express 访问数据库(五):TIBTransaction
java·数据库·express
^velpro^6 小时前
数据库连接池的创建
java·开发语言·数据库
苹果醋36 小时前
Java8->Java19的初步探索
java·运维·spring boot·mysql·nginx