【多线程案例】定时器应用及实现

文章目录

    • 1. 定时器是什么?
    • 2. 定时器的应用
    • 3. 自己实现定时器

1. 定时器是什么?

定时器就类似生活中的闹钟,它是软件开发中的一个重要组件。当有些线程我们并不希望它立刻执行,这个时候我们就可以使用定时器,规定线程在多长时间后执行。
【多线程案例】定时器应用及实现_第1张图片

2. 定时器的应用

定时器在Java标准库中提供的有一个Timer类。Timer中核心的方法是schedule,schedule中有俩个参数,一个是参数是执行任务代码,另一个参数是指定代码多少毫秒后执行。

public class Test {
    public static void main(String[] args) {
        Timer timer = new Timer();
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                System.out.println("3秒!");
            }
        },3000);
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                System.out.println("2秒");
            }
        },2000);
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                System.out.println("1秒");
            }
        },1000);
        System.out.println("开始");

    }
}

运行结果:
【多线程案例】定时器应用及实现_第2张图片

3. 自己实现定时器

自己实现一个定时器能够帮助我们更好的理解定时器。实现定时器之前我们要知道定时器的构成。

  1. 一个带优先级的堵塞队列;(后面说为什么使用优先级堵塞队列)
  2. 队列中元素都是一个个要执行的任务;
  3. 队列属性要带有时间属性;
  4. 同时有个线程不停扫描队列元素,找到要执行的任务。

为什么使用优先级堵塞队列?
如果使用普通队列:在执行任务前,扫描线程不停的遍历队列,找到要执行的任务,在遍历时,线程执行的速度时非常快的,这个时候就非常浪费CPU资源。
使用优先级堵塞队列:那么,我们的扫描线程只需要扫描队列的队头,因为 在这个优先级堵塞队列中可以使最先执行的任务放在队头,而且在扫描到这个对头后,我们也可以进入休眠,更一步减少资源浪费。

class MyTimerTask implements Comparable<MyTimerTask>{
    //TimerTask的两个参数
    private Runnable runnable;
    private long time;
    //实例化
    public MyTimerTask(Runnable runnable, long time){
        this.runnable = runnable;
        //计算要执行的时间
        this.time = System.currentTimeMillis() + time;

    }

    public Runnable getRunnable() {
        return runnable;
    }

    public long getTime() {
        return time;
    }
    //比较

    @Override
    public int compareTo(MyTimerTask o) {
        return (int) (time - o.time);
    }
}
class MyTimer{
    //带优先级的堵塞队列
    PriorityQueue<MyTimerTask> queue = new PriorityQueue<>();
    //锁
    Object locker = new Object();
    //实现Timer类中的schedule的方法,接收任务和时间两个参数
    public void schedule(Runnable runnable, long time){
        //多线程环境,对队列操作都加锁,保证线程安全
        synchronized (locker) {
            //任务时间
            MyTimerTask myTimerTask = new MyTimerTask(runnable, time);
            //加入到队列
            queue.offer(myTimerTask);
            //每次添加元素,都唤醒一次循环队列,防止超时
            locker.notify();
        }
    }
    public MyTimer(){
        //在实例化一个MyTimer,那么就就会启动这个扫描线程,进行扫描
        Thread t = new Thread(() -> {
            //使用循环,扫描线程并不是只执行一次
            while (true) {
                try {
                    //加锁,保证线程安全
                    synchronized (locker) {
                        //使用while,不要使用if,防止出现非线程安全
                        while (queue.isEmpty()) {
                            //队列为空,没有要执行的任务,进行等待,堵塞
                            locker.wait();
                        }
                        //取到队列对头,但不出队列,并不一定到时间执行
                        MyTimerTask myTimerTask = queue.peek();
                        //现在的时间
                        long time = System.currentTimeMillis();
                        //现在的时间如果大于任务要执行的时间,队头便出队列
                        if (time >= myTimerTask.getTime()) {
                            queue.poll();
                            //执行任务
                            myTimerTask.getRunnable().run();
                        } else {
                            //如果不到时间,进行等待,计算时间差,防止还没到就被唤醒,带来资源浪费
                            locker.wait(myTimerTask.getTime() - time);
                        }
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        //启动!!!
        t.start();
    }
}
public class Test3 {
    public static void main(String[] args) {
        MyTimer myTimer = new MyTimer();
        myTimer.schedule(new Runnable() {
            @Override
            public void run() {
                System.out.println("3秒!");
            }
        },3000);
        myTimer.schedule(new Runnable() {
            @Override
            public void run() {
                System.out.println("2秒");
            }
        },2000);
        myTimer.schedule(new Runnable() {
            @Override
            public void run() {
                System.out.println("1秒");
            }
        },1000);
        System.out.println("开始!");
    }
}

运行结果:
【多线程案例】定时器应用及实现_第3张图片

你可能感兴趣的:(Java多线程编程,多线程,线程安全,定时器)