Java定时器

这篇具有很好参考价值的文章主要介绍了Java定时器。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

目录

什么是定时器?

如何使用定时器?

schedule

Timer的构造方法

cancel

定时器的模拟实现

思路分析

实现过程

完整代码


什么是定时器?

定时器:即在设定的时间时执行某事的设备(例如闹钟,在指定的时间响铃),Java中的定时器会在到达设定的时间后,执行指定的代码

Java标准库中提供了一个定时器 Timer类 供我们使用,位于java.util中

如何使用定时器?

schedule

对于Timer类,其核心方法为schedule

public void schedule(TimerTask task, long delay)

其中包含两个参数,

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

Java定时器,JavaEE,开发语言,java,定时器,多线程

Java定时器,JavaEE,开发语言,java,定时器,多线程

 task 即到达时间后要执行的任务代码,其必须是 TimerTask 的子类,通过继承TimerTask类并重写run()方法来指定具体的任务

delay即指定要等待的时间(单位为毫秒)

Timer timer = new Timer();
timer.schedule(new TimerTask() {
    @Override
    public void run() {
        System.out.println("3000");
    }
},3000);//3秒后打印3000
public void schedule(TimerTask task, Date time)

 在time时间执行task任务一次

public void schedule(TimerTask task, long delay, long period) 

在delay后执行task一次,之后每period时间后执行task

public void schedule(TimerTask task, Date firstTime, long period)

 在firstTime时执行task一次,之后每period时间后执行task,若时间为过去时间,则会立即执行

Timer的构造方法

Timer timer = new Timer()

public Timer() {
    this("Timer-" + serialNumber());
}

调用this("Timer-" + serialNumber()),以 Timer- +序列号作为定时器的名字

Timer timer = new Timer(String name) 以name作为定时器的名字

Timer timer = new Timer(boolean isDeamon) 是否将该定时器作为守护线程执行

Timer timer = new Timer(String name, boolean isDeamon) 以name作为定时器名字,是否将该定时器作为守护线程

cancel

cancel方法用于终止Timer线程

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

//定时器的使用
public class Demo {
    public static void main(String[] args) throws InterruptedException {
        Timer timer = new Timer();
        Date date = new Date(System.currentTimeMillis());
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                System.out.println("hello");
            }
        },date,2000);
        Thread.sleep(5000);//若主线程不休眠,直接执行cancel方法,则定时器还来不及执行就被关闭了
        timer.cancel();
    }
}

注:

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

2. Timer是线程安全的

定时器的模拟实现

了解了什么是定时器和定时器的使用之后,那么定时器是如何实现的呢?

我们通过模拟实现定时器来进一步了解定时器的原理

这里我们仅模拟 Timer 不带参数的构造方法 和 等待delay时间后执行task的schedule

思路分析

要想实现定时器,首先我们要分析定时器需要完成的功能,以及如何实现这些功能

Timer类通过schedule添加等待delay时间后执行的任务代码,因此我们需要一个容器来存放这些任务,且先到达指定时间的代码先执行,因此我们可以使用优先级队列来存放 task(其中task带有时间属性,记录任务执行的时间),队首元素是最先执行的任务

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

因此,模拟定时器需完成:

1. 优先级队列,用于存放task,队首元素是最先执行的任务

2. task中带有时间属性,记录task执行的时间

3. 线程worker 扫描队首元素,判断队首元素是否需要执行

4. 保证定时器线程安全

实现过程

我们首先创建MyTimer:

class MyTimer{
    private Thread worker = null;//用来扫描队首元素,判断其是否需要执行
    //任务队列,优先执行时间短的任务线程
    private PriorityQueue<MyTimerTask> queue = new PriorityQueue<>();
    public void schedule(Runnable runnable, long delay){
      
    }
}

MyTimer中包含一个优先级队列,其中存放任务task,因此我们创建MyTimer类:

MyTimerTask类用于描述一个任务(作为Timer的内部类),里面包含一个Runnable对象和一个time(毫秒时间戳)(由于传入的delay是等待时间,因此要将其转换为执行时间)

class MyTimerTask{
    private long time;//ms级别的时间戳
    private Runnable runnable;//要执行的代码
    //构造方法
    public MyTimerTask(Runnable runnable, long delay){
        this.runnable = runnable;
        //计算要执行的时间
        this.time = System.currentTimeMillis() + delay;
    }

    //run方法
    public void run(){
        runnable.run();
    }

    public long getTime(){
        return  time;//返回时间戳
    }
}

由于MyTimerTask对象要放到优先级队列中,因此必须可比较这里我们实现Comparable接口,使其可以进行比较

重写其中的compareTo方法,让执行时间小的元素优先出队列

@Override
public int compareTo(MyTimerTask o) {
    return (int)(this.time - o.time);
}

接下来我们实现schedule方法,

schedule方法要实现的功能为:将新增的任务添加到队列中

//通过schedule,添加要执行的线程
public void schedule(Runnable runnable, long delay){
    MyTimerTask task = new MyTimerTask(runnable, delay);
    //将任务添加到队列中
    queue.offer(task);
}

然后我们来实现MyTimerTask的构造方法:

在构造方法中,我们要实现的功能有:

1. 扫描队首元素,判断其是否到达执行时间,若到达执行时间,就执行任务代码,若未到达时间,则等待

由于我们要保证线程安全,因此我们需要相关操作进行加锁

在这里,我们通过创建锁对象进行加锁(也可以通过this进行加锁)

我们实现实现worker扫描队首元素

worker要反复扫描队首元素,然后判断队首元素是否到达指定时间

public MyTimer(){
    worker = new Thread(()-> {
        //反复扫描队首元素,然后判定队首元素是否到时间
        //未到时间,等待
        //到时间,执行任务并将其从任务队列中删除
        while (true) {
            if (queue.isEmpty()) {
                //队列为空,要等待添加任务

            }
            //队列不为空,获取队首元素
            MyTimerTask task = queue.peek();
            //获取当前时间
            long curTime = System.currentTimeMillis();
            //判断是否到任务时间
            if (curTime >= task.getTime()) {
                task.run();
                queue.poll();
            } else {
                //未到任务执行时间,等待

            }
        }
    });
    worker.start();
}

在判断队列为空时,要等待调用schedule方法向队列中添加元素后才解除阻塞状态,因此我们可以使用wait()方法,等待schedule唤醒,然后也可能由于其他原因被意外唤醒,因此我们使用while循环来判断队列是否为空,在结束阻塞状态后,再进行一次判断,保证队列不为空

而在未到达任务时间时,则使用 wait(task.getTime() - curTime) 等待指定时间后再解除阻塞状态,然后再进行判断。而若是在等待期间插入了新的任务,也需要解除阻塞状态,判断新插入的是否需要先执行、是否到达执行时间

因此,在schedule方法中,完成添加任务操作后,需要唤醒阻塞的线程

而在执行添加任务操作时,也需要进行加锁,保证线程安全

    public void schedule(Runnable runnable, long delay){
        synchronized (locker) {
            MyTimerTask task = new MyTimerTask(runnable, delay);//将任务添加到队列中
            queue.offer(task);
            //将任务添加到队列中后,就可以唤醒阻塞的扫描线程了
            locker.notify();
        }
    }

接下来,我们对构造方法中的判断和执行操作进行加锁

       public MyTimer(){
        worker = new Thread(()->{
            //扫描线程反复扫描队首元素,然后判定队首元素是否到时间
            //未到时间,阻塞
            //到时间,执行任务并将其从任务队列中删除
            while (true) {
                try{
                    synchronized (locker) {
                        while (queue.isEmpty()) {
                            //阻塞等待
                            locker.wait();
                        }
                        MyTimerTask task = queue.peek();
                        //获取当前时间
                        long curTime = System.currentTimeMillis();
                        //判断是否到任务时间
                        if (curTime >= task.getTime()) {
                            task.run();
                            queue.poll();
                        } else {
                            //阻塞等待
                            locker.wait(task.getTime() - curTime);

                        }
                    }
                }catch (InterruptedException e){
                    e.printStackTrace();
                }

            }
        });
        worker.start();
    }

完整代码

MyTimerTask:

class MyTimerTask implements Comparable<MyTimerTask>{
    private long time;//ms级别的时间戳
    private Runnable runnable;//要执行的代码
    //构造方法
    public MyTimerTask(Runnable runnable, long delay){
        this.runnable = runnable;
        //计算要执行的绝对时间
        this.time = System.currentTimeMillis() + delay;
    }

    //run方法
    public void run(){
        runnable.run();
    }

    public long getTime(){
        return  time;//返回时间戳
    }

    //重写compareTo,通过时间进行比较

    @Override
    public int compareTo(MyTimerTask o) {
        return (int)(this.time - o.time);
    }
}

MyTimer:文章来源地址https://www.toymoban.com/news/detail-774859.html

//模拟实现定时器
class MyTimer{
    private Thread worker = null;//用来扫描队首元素,判断其是否需要执行
    //任务队列,优先执行时间短的任务线程
    private PriorityQueue<MyTimerTask> queue = new PriorityQueue<>();
    //创建锁对象
    private Object locker = new Object();

    //通过schedule,添加要执行的线程
    public void schedule(Runnable runnable, long delay){
        synchronized (locker) {
            MyTimerTask task = new MyTimerTask(runnable, delay);//将任务添加到队列中
            queue.offer(task);
            //将任务添加到队列中后,就可以唤醒阻塞的扫描线程了
            locker.notify();
        }
    }

    //构造方法,创建扫描线程,让扫描线程进行判定和执行
        public MyTimer(){
        worker = new Thread(()->{
            //扫描线程反复扫描队首元素,然后判定队首元素是否到时间
            //未到时间,阻塞
            //到时间,执行任务并将其从任务队列中删除
            while (true) {
                try{
                    synchronized (locker) {
                        while (queue.isEmpty()) {
                            //阻塞等待
                            locker.wait();
                        }
                        MyTimerTask task = queue.peek();
                        //获取当前时间
                        long curTime = System.currentTimeMillis();
                        //判断是否到任务时间
                        if (curTime >= task.getTime()) {
                            task.run();
                            queue.poll();
                        } else {
                            //阻塞等待
                            locker.wait(task.getTime() - curTime);

                        }
                    }
                }catch (InterruptedException e){
                    e.printStackTrace();
                }

            }
        });
        worker.start();
    }
}

到了这里,关于Java定时器的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【Java | 多线程案例】定时器的实现

    个人主页:兜里有颗棉花糖 欢迎 点赞👍 收藏✨ 留言✉ 加关注💓本文由 兜里有颗棉花糖 原创 收录于专栏【Java系列专栏】【JaveEE学习专栏】 本专栏旨在分享学习JavaEE的一点学习心得,欢迎大家在评论区交流讨论💌 Java中, Timer类 是用于计划和执行重复任务的类( Java标准

    2024年02月03日
    浏览(29)
  • 【Java】多线程案例(单例模式,阻塞队列,定时器,线程池)

    ❤️ Author: 老九 ☕️ 个人博客:老九的CSDN博客 🙏 个人名言:不可控之事 乐观面对 😍 系列专栏: 单例模式是设计模式之一。代码当中的某个类,只能有一个实例,不能有多个。单例模式分为:饿汉模式和懒汉模式 饿汉模式表示很着急,就想吃完饭剩下很多碗,然后一

    2024年02月06日
    浏览(35)
  • Java 多线程系列Ⅳ(单例模式+阻塞式队列+定时器+线程池)

    设计模式就是软件开发中的“棋谱”,软件开发中也有很多常见的 “问题场景”。针对这些问题场景,大佬们总结出了一些固定的套路。按照这些套路来实现代码可能不会很好,但至少不会很差。当前阶段我们需要掌握两种设计模式: (1)单例模式 (2)工厂模式 概念/特征

    2024年02月09日
    浏览(42)
  • 【Java|多线程与高并发】定时器(Timer)详解

    在Java中,定时器 Timer 类是用于执行定时任务的工具类。它允许你安排一个任务在未来的某个时间点执行,或者以固定的时间间隔重复执行。 在服务器开发中,客户端向服务器发送请求,然后等待服务器响应. 但服务器什么时候返回响应,并不确定. 但也不能让客户端一直等下去

    2024年02月07日
    浏览(36)
  • JAVAEE-定时器案例

    设置一个时间,当时间到了的时候,定时器就会去自动执行某个逻辑. 这里timer里面的任务执行完了也并不会结束,因为他并不知道是否还会添加新的任务进去, 处在严阵以待的状态 此时如果我们想要结束,应该使用cancel主动去结束. 1)需要有一个线程,负责掐时间,等任务到达合适的

    2024年02月05日
    浏览(32)
  • 【JavaEE初阶】 定时器详解与实现

    定时器也是软件开发中的一个重要组件. 类似于一个 “闹钟”. 达到一个设定的时间之后, 就执行某个指定好的代码 定时器是一种实际开发中非常常用的组件. 比如网络通信中, 如果对方 500ms 内没有返回数据, 则断开连接尝试重连. 比如一个 Map, 希望里面的某个 key 在 3s 之后过

    2024年02月06日
    浏览(25)
  • Java定时器

    目录 什么是定时器? 如何使用定时器? schedule Timer的构造方法 cancel 定时器的模拟实现 思路分析 实现过程 完整代码 定时器: 即在设定的时间时执行某事的设备(例如闹钟,在指定的时间响铃),Java中的定时器会在到达设定的时间后,执行指定的代码 Java标准库中提供了一

    2024年02月03日
    浏览(28)
  • Java定时器 @Scheduled注解的使用

    @Scheduled注解可以用于做定时任务,再方法上加上@Scheduled注解,可以将这个方法定义为一个任务发放,可以搭配cron表达式进行任务的控制。 开启定时任务时在类上加注解 @EnableScheduling 1.按顺序依次为 秒 分 时 天 月 周 年 表达式长度为6个或者7个 cron表达式是一个字符串,分为

    2024年02月04日
    浏览(35)
  • 【多线程】定时器,详解定时器原理,让大家更深刻的理解多线程

    前言: 大家好,我是 良辰丫 ,今天我们一起了解一下定时器,通过定时器来熟悉一下线程安全等相关知识点.💞💞💞 🧑个人主页:良辰针不戳 📖所属专栏:javaEE初阶 🍎励志语句:生活也许会让我们遍体鳞伤,但最终这些伤口会成为我们一辈子的财富。 💦期待大家三连,关注

    2024年02月01日
    浏览(41)
  • 多线程---定时器

    定时器的功能和“闹钟”类似,代码中的定时器通常都是“多长时间之后,执行某个动作”。 注: 一个定时器可以安排多个任务。 调用schedule安排任务时,一定要重写run方法,明确任务内容 定时器开启后不会自动结束,得手动杀死进程 版本一:实现简单的定时器 schedule方法

    2024年02月08日
    浏览(30)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包