java多线程案例-定时器

文章介绍了Java中的定时器Timer类,用于在指定时间后执行任务。Timer类的核心方法是schedule,它使用TimerTask来定义要执行的任务。文章还通过代码示例展示了如何创建和使用定时器,并解释了其内部机制,包括使用带优先级的阻塞队列来高效管理待执行的任务。
摘要由CSDN通过智能技术生成

1441607163859940

🍁 一、定时器概论

1.什么是定时器

定时器是软件开发中的一个重要组件。类似于一个 “闹钟”,达到一个设定的时间之后, 就执行某个指定好的代码

  • 访问某个网站出现了网络卡顿,浏览器就会转圈圈(阻塞等待),这个等待不是无限的等待,到达一定时间以后,就显示超时访问
  • 在前端开发中网站上的动画效果,也是通过定时器实现的,比如每隔30ms,把页面往下滚动几个像素

2.Java标准库中的定时器——Timer

  • 标准库中提供了一个 Timer 类

构造方法

构造方法备注
1public Timer()无参, 定时器关联的线程为前台线程, 线程名为默认值.
2public Timer(boolean isDaemon)指定定时器中关联的线程类型, true(后台线程), false(前台线程).
3public Timer(String name)指定定时器关联的线程名, 线程类型为前台线程
4public Timer(String name, boolean isDaemon)指定定时器关联的线程名和线程类型

如果没有指定定时器中关联的线程类型,默认为false(前台线程)

前台线程:前台线程会影响java进程的结束,只有在前台线程结束后,java线程才能结束。

  • Timer 类的核心方法为 schedule,schedule 方法是给Timer注册一个任务, 这个任务在指定时间后进行执行, TimerTask类就是专门描述定时器任务的一个抽象类, 它实现了Runnable接口.
public abstract class TimerTask implements Runnable

3.代码演示定时器

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

GIF 2023-3-25 17-10-53

通过结果我们看到了定时器的作用,就是让一个任务在某一个时刻执行。

同时,我们发现这个代码没有运行结束,就是因为这是前台线程,会影响java进程的结束。

🍁 二、定时器的实现

定时器的构成:

  • 队列中的每个元素是一个 Task 对象,Task中带有一个时间属性和一个Runnable任务属性
  • 带有优先级的阻塞队列

为啥要带优先级呢?
因为阻塞队列中的任务都有各自的执行时刻 (delay)。最先执行的任务一定是 delay 最小的. 使用带优先级的队列就可以高效的把这个 delay 最小的任务找出来,使用带有阻塞功能的优先队列用以维护线程安全

  • schedule方法,该方法用于往队列中插入元素
  • 扫描线程不断去扫描队首元素,看看队首元素是不是已经到点了,如果到点就执行这个任务,如果没有到点,就把这个队首元素塞回队列中,继续扫描
// 表示一个任务.
class MyTask implements Comparable<MyTask> {
    public Runnable runnable;
    // 为了方便后续判定, 使用绝对的时间戳.
    public long time;


    public MyTask(Runnable runnable, long delay) {
        this.runnable = runnable;
        // 取当前时刻的时间戳 + delay, 作为该任务实际执行的时间戳
        this.time = System.currentTimeMillis() + delay;
    }

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

class MyTimer {
    //带有优先级的阻塞队列
    private PriorityBlockingQueue<MyTask> queue = new PriorityBlockingQueue<>();

    // 创建一个锁对象
    private Object locker = new Object();

    // 此处的 delay 是一个形如 3000 这样的数字 (多长时间之后, 执行该任务)
    public void schedule(Runnable runnable, long delay) {
        // 根据参数, 构造 MyTask, 插入队列即可.
        MyTask myTask = new MyTask(runnable, delay);
        queue.put(myTask);
        synchronized (locker) {
            locker.notify();
        }
    }


    // 在这里构造线程, 负责执行具体任务了.
    public MyTimer() {
        Thread t = new Thread(() -> {
            while (true) {
                try {
                    // 阻塞队列, 只有阻塞的入队列和阻塞的出队列, 没有阻塞的查看队首元素.
                    MyTask myTask = queue.take();
                    long curTime = System.currentTimeMillis();
                    if (myTask.time <= curTime) {
                        // 时间到了, 可以执行任务了
                        myTask.runnable.run();
                    } else {
                        // 时间还没到
                        // 把刚才取出的任务, 重新塞回队列中.
                        queue.put(myTask);
                        synchronized (locker) {
                            locker.wait(myTask.time - curTime);
                        }
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        t.start();
    }
}

测试:

   public static void main(String[] args) {
        MyTimer myTimer = new MyTimer();
        myTimer.schedule(new Runnable() {
            @Override
            public void run() {
                System.out.println("hello4");
            }
        }, 4000);
        myTimer.schedule(new Runnable() {
            @Override
            public void run() {
                System.out.println("hello3");
            }
        }, 3000);
        myTimer.schedule(new Runnable() {
            @Override
            public void run() {
                System.out.println("hello2");
            }
        }, 2000);
        myTimer.schedule(new Runnable() {
            @Override
            public void run() {
                System.out.println("hello1");
            }
        }, 1000);
    }

GIF 2023-3-25 17-28-15

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值