首页 > 基础资料 博客日记

Java 多线程(七)—— 定时器

2024-10-31 23:00:09基础资料围观66

本篇文章分享Java 多线程(七)—— 定时器,对你有帮助的话记得收藏一下,看Java资料网收获更多编程知识

定时器介绍与使用

先简单介绍一下什么是定时器:定时器类似生活中的闹钟,当时间一到,我们就会去做某些事情。
在代码层面理解就是,当我们设置的时间一到,程序就会执行我们固定的代码片段(也就是任务)

在Java 标准库中给我们提供了定时器的类 Timer

下面是构造方法:

Timer() 就是直接创建一个定时器,里面的属性都是默认值
Timer(boolean isDaemon) 设置定时器里的线程是否为守护线程(后台线程)
Timer(String name) 给你的定时器设置一个名字


schedule

schedule 方法是 Timer 的核心方法,这里介绍第一个schedule 方法的参数含义。

首先 TimerTask task 是 要执行的任务,delay 是时间设置,单位是毫秒(ms)

意思就是在多少毫秒之后就会执行哪些任务。

TimerTask 其实就是对 Runnable 的进一步封装

因此我们在传递task 的时候,要重写 run 方法

代码演示:

public class Demo3 {
    public static void main(String[] args) {
        Timer timer = new Timer();
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                System.out.println("hello");
            }
        }, 5000);
    }
}

程序启动之后,5s 之后就会打印 hello

模拟实现

定时器需要定时执行一些任务,这些任务按照各自的时间执行,因此我们可以使用优先级队列来管理这些任务。
为了便于我们实现任务类(MyTimerTask)这里就不采用抽象类的写法,直接定义两个成员变量,一个是 Runnable 任务,一个是 delay 时间,并且在 MyTimerTask 这里实现 Comparable 接口,当然你也可以自己实现一个 Comparator

class MyTimerTask implements Comparable<MyTimerTask>{
    private Runnable task;
    private long delay;

    public MyTimerTask(Runnable task, long delay) {
        this.task = task;
        this.delay = delay;
    }

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

    public Runnable getTask() {
        return task;
    }

    public long getDelay() {
        return delay;
    }
}

在模拟实现定时器类中,先定义一个优先级队列。

接着我们来写schedule 方法,首先schedule 需要传入两个参数,在Java 源码里是直接传入 task 对象,这里简单一点,直接传入 Runnable ,然后再传入一个 delay 延时时间。

在这个方法中实例化我们的 MyTimerTask 对象,注意时间我们不能直接传过去,我们需要处理一下时间,把 delay + System.currentTimeMillis() 作为时间传入,System.currentTimeMillis() 是系统此时的时间,加上 delay 就是这个任务要在哪个时刻开始执行。

为了避免因为多个线程同时传入任务而导致线程安全问题,这里我们进行加锁,为什么要使用 notify ,后面会提到。

    public synchronized void schedule(Runnable task, long delay) {
        MyTimerTask timerTask = new MyTimerTask(task, System.currentTimeMillis() + delay);
        queue.offer(timerTask);
        this.notify();
    }

最后就是在定时器里创建一个线程,这里我直接在定时器实例化的时候把线程创建出来。

线程的任务就是不断从队列里获取任务然后去执行,因此当队列没有元素的时候,我们需要进行线程等待状态,什么时候结束等待呢?就是当队列添加了一个元素之后,就发出 notify 信号结束等待,所以上面的 schedule 方法最后一行代码是 this.notify() 就是为了提醒这里要结束等待。

结束等待之后,我们就要获取堆顶元素,这里先不着急 poll(),因为我们还不确定这个任务是否是现在就立刻执行,所以我们先 peek(),接着进行判断,如果 task.getDelay() <= System.currentTimeMillis() 的时候就执行任务,否则就是 task.getDelay() > System.currentTimeMillis() 那就进入线程等待,此时我们不能采取死等策略,因为这时候可能没人会发出 notify() 来唤醒这个线程,除了 schedule 方法里 有 notify 之外,可是 schedule 方法的 notify 是起到因为 队列添加了元素,队列不为空唤醒上一个 wait 的作用,因此如果这时候没有人 schedule 的话,这个线程就永远都不会被唤醒了。

因此这里采取有时间的 wait ,this.wait(task.getDelay() - System.currentTimeMillis());,可是我们知道这个 wait 是有可能因为 schedule 唤醒的,这时候后面的代码不能直接写 任务执行,万一此时队列添加了一个更早的元素的话,按道理你执行的任务就应该更新,**并且此时时间一定没到,wait 提前被唤醒,也是不能执行任务的,**所以这里我们采取 if - else 代码块来写这里的代码,当线程确实被唤醒之后,没事,再走一遍上面的流程重新获取堆顶元素即可。

    public MyTimer() {
        Thread t = new Thread(() -> {
            try {
                while(true) {
                    synchronized (this) {
                        while (queue.isEmpty()) {
                            this.wait();
                        }
                        MyTimerTask task = queue.peek();
                        if(task.getDelay() > System.currentTimeMillis()) {
                            this.wait(task.getDelay() - System.currentTimeMillis());
                        } else {
                            task.getTask().run();
                            queue.poll();
                        }
                    }
                }
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        });
        t.start();
    }

最终定时器模拟实现的代码:

class MyTimerTask implements Comparable<MyTimerTask>{
    private Runnable task;
    private long delay;

    public MyTimerTask(Runnable task, long delay) {
        this.task = task;
        this.delay = delay;
    }

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

    public Runnable getTask() {
        return task;
    }

    public long getDelay() {
        return delay;
    }
}

class MyTimer {
    private PriorityQueue<MyTimerTask> queue = new PriorityQueue<>();

    public MyTimer() {
        Thread t = new Thread(() -> {
            try {
                while(true) {
                    synchronized (this) {
                        while (queue.isEmpty()) {
                            this.wait();
                        }
                        MyTimerTask task = queue.peek();
                        if(task.getDelay() > System.currentTimeMillis()) {
                            this.wait(task.getDelay() - System.currentTimeMillis());
                        } else {
                            task.getTask().run();
                            queue.poll();
                        }
                    }
                }
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        });
        t.start();
    }

    public synchronized void schedule(Runnable task, long delay) {
        MyTimerTask timerTask = new MyTimerTask(task, System.currentTimeMillis() + delay);
        queue.offer(timerTask);
        this.notify();
    }
}

文章来源:https://blog.csdn.net/liwuqianhzc/article/details/143205964
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:jacktools123@163.com进行投诉反馈,一经查实,立即删除!

标签:

相关文章

本站推荐

标签云