【JavaEE】多线程案例 - 定时器

news/2024/7/5 0:46:00

作者主页:paper jie_博客

本文作者:大家好,我是paper jie,感谢你阅读本文,欢迎一建三连哦。

本文于《JavaEE》专栏,本专栏是针对于大学生,编程小白精心打造的。笔者用重金(时间和精力)打造,将MySQL基础知识一网打尽,希望可以帮到读者们哦。

其他专栏:《MySQL》《C语言》《javaSE》《数据结构》等

内容分享:本期将会分享多线程案例 - 定时器

目录

 什么是定时器

Java标准库中的定时器 - Timer

自定义一个定时器

定时器的组成

描述类MyTask

实现MyTask的比较

MyTimer类的构架

schedule方法

内置线程

线程安全与wait等待

具体代码

代码执行流程


 什么是定时器

定时器是我们程序猿来软件开发中一个很重要的组件.它的作用就是和闹钟一样. 当达到一个设定的时间后,就需要去执行某段指定的代码.定时器在我们实际开发中特别常见.比如一款游戏需要联网才能使用,要是在1秒之内没有数据返回给服务器,这时定时器就会发挥作用,断开与网络的连接然后尝试重连.

Java标准库中的定时器 - Timer

在我们Java标准库中就内置了一个Timer类,他就是定时器. 它里面有一个核心方法schedule就是用来注册任务的. 

schedule里面有两个参数. 一个是时间到了需要执行的代码, 第二个是需要等待的时间,单位是毫秒.

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

自定义一个定时器

定时器的组成

这里用一个MyTimer类来表示定时器

1) 需要一个存放任务的优先级队列PriorityQueue

2) 需要一个MyTask类来描述任务

3) 这些任务是需要比较时间的,MyTask类需要实现Comparable接口

4) 需要定义一个内置线程来不断扫描任务观察时间到了没

5) 需要实现核心方法schedule来注册任务

描述类MyTask

Task这个类用来描述任务,里面包含一个Runnable对象和一个time时间戳. 需要执行的代码会通过传参的形式给到Runnable.

class MyTask3 {
    //
    private long time;
    private Runnable runnable = null;
    public MyTask3(Runnable runnable, long delay) {
        this.runnable = runnable;
        //需要执行的时刻
        this.time = System.currentTimeMillis() + delay;
    }
    //获取时间
    public long getTime() {
        return time;
    }
    //获取需要执行的代码
    public void run() {
        runnable.run();
    }
    
}

实现MyTask的比较

因为Mytask是描述任务,而这些任务需要放到优先级队列中比较,就需要实现Comparable或者比较器.这里我们实现Comparable接口.

class MyTask3 implements Comparable<MyTask3>{
    //
    private long time;
    private Runnable runnable = null;
    public MyTask3(Runnable runnable, long delay) {
        this.runnable = runnable;
        //需要执行的时刻
        this.time = System.currentTimeMillis() + delay;
    }
    //获取时间
    public long getTime() {
        return time;
    }
    //获取需要执行的代码
    public void run() {
        runnable.run();
    }
    //比较时间快慢方法
    @Override
    public int compareTo(MyTask3 o) {
        return (int)(this.time - o.time);
    }
}

MyTimer类的构架

这里最核心的就是priorityQueue这个优先级队列,用它来存放我们的任务. 所对象为我们后面起到一个加锁的作用.

class MyTimer3 {
    //用优先级队列来存放任务
    private PriorityQueue<MyTask3> priorityQueue = new PriorityQueue<>();
    //锁对象
    private Object blocker = new Object();
    public void schedule(Runnable runnable, long time) {
        //核心方法
    }
}

schedule方法

这里我们在核心方法schedule中创建出一个任务,再将这个任务放入优先级队列中.

public void schedule(Runnable runnable, long time) {
        //创建一个任务
        MyTask3 myTask3 = new MyTask3(runnable, time);
        //将创建的任务放入优先级队列中
        priorityQueue.offer(myTask3);
    }

内置线程

这里将内置线程放入构造方法中,当这个类一创建就开始执行. 这里通过while循环来不断扫描.

//内置线程
    public MyTimer3() {
        //创建一个线程
        Thread t = new Thread(() -> {
            //通过while来不断扫描
            while(true) {
                //判断优先级队列是不是空的
                if(priorityQueue.isEmpty()) {
                    //为空就等待
                    //continue;
                }
                //当优先级队列中有任务时,取出任务
                MyTask3 myTask3 = priorityQueue.peek();
                //当前时间
                long time = System.currentTimeMillis();
                //如果到时间了就执行
                if(time >= myTask3.getTime()) {
                    myTask3.run();
                    priorityQueue.take();
                }else {
                    //时间没到等待
                    //continue;
                }
                
            }
        });
        t.start();
    }

线程安全与wait等待

这里发现schedule方法和构造方法都会有对于priorityQueue优先级队列的读和修改,这里可能就会出现线程安全问题,我们就需要为他们加上锁. 为空时我们就通过wait方法等待,等调用schedule方法使用notify唤醒它. 当有元素时但时间没到也是使用wait有时间的等待,时间到了就解除等待.

public void schedule(Runnable runnable, long time) {
        synchronized (blocker) {
            //创建一个任务
            MyTask3 myTask3 = new MyTask3(runnable, time);
            //将创建的任务放入优先级队列中
            priorityQueue.offer(myTask3);
            //通过notify来唤醒扫描线程
            blocker.notify();
        }
    }

    //内置线程
    public MyTimer3() {
        //创建一个线程
        Thread t = new Thread(() -> {
            //通过while来不断扫描
            while(true) {
                //加锁
                synchronized (blocker) {
                    try {
                        //判断优先级队列是不是空的
                        if(priorityQueue.isEmpty()) {
                            //为空就等待
                            blocker.wait();
                        }
                        //当优先级队列中有任务时,取出任务
                        MyTask3 myTask3 = priorityQueue.peek();
                        //当前时间
                        long time = System.currentTimeMillis();
                        //如果到时间了就执行
                        if(time >= myTask3.getTime()) {
                            myTask3.run();
                            priorityQueue.poll();
                        }else {
                            //时间没到等待 通过wait等待 有时间的等待.
                            blocker.wait(myTask3.getTime() - time);
                        }
                    }catch(InterruptedException o) {
                        o.printStackTrace();
                    }
                }
            }
        });
        //启动线程
        t.start();
    }

具体代码

class MyTask3 implements Comparable<MyTask3>{
    //
    private long time;
    private Runnable runnable = null;
    public MyTask3(Runnable runnable, long delay) {
        this.runnable = runnable;
        //需要执行的时刻
        this.time = System.currentTimeMillis() + delay;
    }
    //获取时间
    public long getTime() {
        return time;
    }
    //获取需要执行的代码
    public void run() {
        runnable.run();
    }
    //比较时间快慢方法
    @Override
    public int compareTo(MyTask3 o) {
        return (int)(this.time - o.time);
    }
}

class MyTimer3 {
    //用优先级队列来存放任务
    private PriorityQueue<MyTask3> priorityQueue = new PriorityQueue<>();
    //所对象
    private Object blocker = new Object();
    //核心方法
    public void schedule(Runnable runnable, long time) {
        synchronized (blocker) {
            //创建一个任务
            MyTask3 myTask3 = new MyTask3(runnable, time);
            //将创建的任务放入优先级队列中
            priorityQueue.offer(myTask3);
            //通过notify来唤醒扫描线程
            blocker.notify();
        }
    }

    //内置线程
    public MyTimer3() {
        //创建一个线程
        Thread t = new Thread(() -> {
            //通过while来不断扫描
            while(true) {
                //加锁
                synchronized (blocker) {
                    try {
                        //判断优先级队列是不是空的
                        if(priorityQueue.isEmpty()) {
                            //为空就等待
                            blocker.wait();
                        }
                        //当优先级队列中有任务时,取出任务
                        MyTask3 myTask3 = priorityQueue.peek();
                        //当前时间
                        long time = System.currentTimeMillis();
                        //如果到时间了就执行
                        if(time >= myTask3.getTime()) {
                            myTask3.run();
                            priorityQueue.poll();
                        }else {
                            //时间没到等待 通过wait等待 有时间的等待.
                            blocker.wait(myTask3.getTime() - time);
                        }
                    }catch(InterruptedException o) {
                        o.printStackTrace();
                    }
                }
            }
        });
        //启动线程
        t.start();
    }
}

代码执行流程



http://lihuaxi.xjx100.cn/news/1899384.html

相关文章

Linux环境下HTTP请求的代码详解与实例

嘿&#xff0c;Linux狂热者们&#xff0c;是时候让我们在这个充满激情的平台上探索一下HTTP协议的奥秘了。我知道&#xff0c;对于这个我们每天都在使用&#xff0c;但却可能没深入了解过的HTTP&#xff0c;你们一定充满了好奇和期待。那么&#xff0c;让我们一起踏上这趟探索之…

人生感悟 | 又是一年,眼看要2024了

哈喽&#xff0c;你好啊&#xff0c;我是雷工&#xff01; 刚过完大雪节气没两天&#xff0c;气温开始急转直下&#xff0c;走在路上明显感觉冷了许多。看天气预报很多地区已经开始下雪了。 看日历已经12月9号了&#xff0c;12月份&#xff0c;一年的最后一个月&#xff0c;2…

有两个循环单链表,链表头指针分别为 h1 和 h2,编写一个函数将 h2 链接到 链表h1 之后,要求处理完仍是一个循环单链表。

题目描述 &#xff1a;有两个循环单链表&#xff0c;链表头指针分别为 h1 和 h2&#xff0c;编写一个函数将 h2 链接到 链表h1 之后&#xff0c;要求处理完仍是一个循环单链表。 分析&#xff1a; 注意题目说的是头指针 h1 和 h2&#xff0c;所以这两个循环单链表并没有头结点…

深入理解GPIO概念详讲

学习目标 了解mos管的使用了解GPIO输出模式了解GPIO输入模式理解ARM系列GPIO相关理论掌握GD32几种GPIO工作模式编码实现学习内容 基础概念 GPIO,全称为“通用输入/输出”(General Purpose Input/Output),是计算机系统中用于与外部世界进行数字通信的一种接口标准。它允许…

WPF——命令commond的实现方法

命令commond的实现方法 属性通知的方式 鼠标监听绑定事件 行为&#xff1a;可以传递界面控件的参数 第一种&#xff1a; 第二种&#xff1a; 附加属性 propa&#xff1a;附加属性快捷方式

AST还原实战|某达购票网址混淆js还原分析

关注它&#xff0c;不迷路。 本文章中所有内容仅供学习交流&#xff0c;不可用于任何商业用途和非法用途&#xff0c;否则后果自负&#xff0c;如有侵权&#xff0c;请联系作者立即删除&#xff01; 1.混淆js地址 https://premier.hkticketing.com/kramericaindustries.…

菜鸟学习日记(python)——匿名函数

Python 使用 lambda 来创建匿名函数。 lambda 函数是一种小型、匿名的内联函数&#xff0c;它可以具有任意数量的参数&#xff0c;但只能有一个表达式。 匿名函数的一般格式如下&#xff1a; lambda 参数列表:表达式 表达式用于计算并返回函数结果 lambda 函数通常用于编写…

centOS7 安装tailscale并启用子网路由

1、在centOS7上安装Tailscale客户端 #安装命令所在官网位置&#xff1a;https://tailscale.com/download/linux #具体命令为&#xff1a; curl -fsSL https://tailscale.com/install.sh | sh #命令执行后如下图所示2、设置允许IP转发和IP伪装。 安装后&#xff0c;您可以启动…