当前位置: 首页 > news >正文

JavaEE初阶第十二期:解锁多线程,从 “单车道” 到 “高速公路” 的编程升级(十)

专栏:JavaEE初阶起飞计划

个人主页:手握风云

目录

一、多线程案例

1.1. 定时器


一、多线程案例

1.1. 定时器

        定时器是软件开发的一个重要组件,是一种能够按照预设的时间间隔或在特定时间点执行某个任务或代码片段的机制。你可以把它想象成一个闹钟,只不过这个“闹钟”不是提醒你去起床,而是提醒计算机去执行某个特定的操作。定时器与阻塞队列一致,也会被单独封装成一个或一组服务器来使用。

  • 标准库中的定时器
import java.util.Timer;
import java.util.TimerTask;public class Demo1 {public static void main(String[] args) {Timer timer = new Timer();timer.schedule(new TimerTask() {@Overridepublic void run() {System.out.println("定时器执行任务 3000");}}, 3000);timer.schedule(new TimerTask() {@Overridepublic void run() {System.out.println("定时器执行任务 2000");}}, 2000);timer.schedule(new TimerTask() {@Overridepublic void run() {System.out.println("定时器执行任务 1000");}}, 1000);System.out.println("程序启动");}
}

  • 定时器的实现

        对于自主实现的定时器,需要指定等待的最大时间,如果等不到,还需要执行其他的操作。

class MyTimer {public MyTimer() {}//向定时器中添加任务public void schedule(Runnable runnable, long delay) {}
}

        这里注意,TimerTask类实现的是Runnable接口,所以我们在schedule方法里面添加的也是Runnable类型的参数。既然要对任务进行组织管理,就得使用合适的数据结构,比如顺序表、栈。但是这些任务不一定是按照时间顺序添加的,并且添加的顺序和执行顺序没太大关系。如果使用顺序表,执行任务时,就需要遍历来找到时间最小的任务,效率太低。这时我们就可以使用堆来解决。

        对于堆所存放的泛型参数,这里不能添加成Runnable,因为堆里面的任务不只是内容,还需要考虑任务的时间。

class MyTimerTask {private Runnable task;// 这个地方为了和当前时间对比,确认任务是否执行,需要保存绝对的时间戳private long time;public MyTimerTask(Runnable task, long delay) {this.task = task;this.time = System.currentTimeMillis() + delay;}public Runnable getTask() {return task;}public long getTime() {return time;}
}class MyTimer {private PriorityQueue<MyTimerTask> queue = new PriorityQueue<>();public MyTimer() {}//向定时器中添加任务public void schedule(Runnable runnable, long delay) {queue.offer(new MyTimerTask(runnable, delay));}
}

        由于MyTimerTask是放在优先级队列中,所以我们还需要写出比较规则。

class MyTimerTask implements Comparable<MyTimerTask>{private Runnable task;// 这个地方为了和当前时间对比,确认任务是否执行,需要保存绝对的时间戳private long time;public MyTimerTask(Runnable task, long delay) {this.task = task;this.time = System.currentTimeMillis() + delay;}public Runnable getTask() {return task;}public long getTime() {return time;}@Overridepublic int compareTo(MyTimerTask o) {return (int) (this.time - o.time);}
}

        接下来就是创建线程,让线程来检测任务是否到时间了,以及去执行这个任务。我们需要循环从队列中取出元素,判断是否到时间了,如果到达就出队列,没有就不做处理。

import java.util.PriorityQueue;class MyTimerTask implements Comparable<MyTimerTask>{private Runnable task;// 这个地方为了和当前时间对比,确认任务是否执行,需要保存绝对的时间戳private long time;public MyTimerTask(Runnable task, long delay) {this.task = task;this.time = System.currentTimeMillis() + delay;}public Runnable getTask() {return task;}public long getTime() {return time;}@Overridepublic int compareTo(MyTimerTask o) {return (int) (this.time - o.time);}
}class MyTimer {private PriorityQueue<MyTimerTask> queue = new PriorityQueue<>();public MyTimer() {Thread t = new Thread(() -> {while (true) {if (queue.isEmpty()) {continue;}MyTimerTask task = queue.peek();long curTime = System.currentTimeMillis();if (curTime < task.getTime()) {continue;} else {task.getTask().run();queue.poll();}}});t.start();}//向定时器中添加任务public void schedule(Runnable runnable, long delay) {queue.offer(new MyTimerTask(runnable, delay));}
}public class Demo2 {public static void main(String[] args) {MyTimer timer = new MyTimer();timer.schedule(new Runnable() {@Overridepublic void run() {System.out.println("定时任务:3000");}}, 3000);timer.schedule(new Runnable() {@Overridepublic void run() {System.out.println("定时任务:2000");}}, 2000);timer.schedule(new Runnable() {@Overridepublic void run() {System.out.println("定时任务:1000");}}, 1000);}
}

        虽然执行效果没有什么问题,但上面的操作对于同一个队列进行出入,所以线程是不安全的。那我们就需要在入队列和出队列的操作里面都要进行加锁。第二个问题,就是忙等出现饿死。如上面的代码,当队列为空或者没到时间时,不做任何处理,只消耗CPU资源,没有任何实质性的进展。尤其是第二个continue,这里就相当于30分钟之后要去执行某项任务,每隔1分钟就得看一下时间,当我们设计了等待时间之后,到时间自动唤醒或者有优先级更高的任务要去执行。

        完整代码:

import java.util.PriorityQueue;class MyTimerTask implements Comparable<MyTimerTask>{private Runnable task;// 这个地方为了和当前时间对比,确认任务是否执行,需要保存绝对的时间戳private long time;public MyTimerTask(Runnable task, long delay) {this.task = task;this.time = System.currentTimeMillis() + delay;}public Runnable getTask() {return task;}public long getTime() {return time;}@Overridepublic int compareTo(MyTimerTask o) {return (int) (this.time - o.time);}
}class MyTimer {private PriorityQueue<MyTimerTask> queue = new PriorityQueue<>();private Object locker = new Object();public MyTimer() {Thread t = new Thread(() -> {while (true) {try {synchronized (locker) {if (queue.isEmpty()) {locker.wait();}MyTimerTask task = queue.peek();long curTime = System.currentTimeMillis();if (curTime < task.getTime()) {locker.wait(task.getTime() - curTime);} else {task.getTask().run();queue.poll();}}} catch (InterruptedException e) {e.printStackTrace();}}});t.start();}//向定时器中添加任务public void schedule(Runnable runnable, long delay) {synchronized (locker) {queue.offer(new MyTimerTask(runnable, delay));locker.notify();}}
}public class Demo2 {public static void main(String[] args) {MyTimer timer = new MyTimer();timer.schedule(new Runnable() {@Overridepublic void run() {System.out.println("定时任务:3000");}}, 3000);timer.schedule(new Runnable() {@Overridepublic void run() {System.out.println("定时任务:2000");}}, 2000);timer.schedule(new Runnable() {@Overridepublic void run() {System.out.println("定时任务:1000");}}, 1000);}
}
http://www.lryc.cn/news/601728.html

相关文章:

  • C++学习(线程相关)
  • 05 - spring security权限控制
  • Java Ai(day04)
  • [Linux入门] Linux 远程访问及控制全解析:从入门到实战
  • 【工具】python汇总发票(含源码)
  • InfluxDB 与 MQTT 协议集成实践(二)
  • Linux网络-------2.应⽤层⾃定义协议与序列化
  • 基于深度学习的图像分割:使用DeepLabv3实现高效分割
  • 【C语言网络编程】HTTP 客户端请求(基于 Socket 的完整实现)
  • 程序代码篇---python向http界面发送数据
  • 【QT入门到晋级】window opencv安装及引入qtcreator(包含两种qt编译器:MSVC和MinGW)
  • 字节前端面试知识点总结
  • 应对反爬机制的具体方法与策略
  • 《 接口日志与异常处理统一设计:AOP与全局异常捕获》
  • Android 调试桥 (adb) 基础知识点
  • 【C 学习】02-究竟什么是C?
  • 【论文阅读】ON THE ROLE OF ATTENTION HEADS IN LARGE LANGUAGE MODEL SAFETY
  • 一文快速了解Docker和命令详解
  • 深度学习中的计算图与自动微分原理:静态图与动态图的实现差异
  • Leetcode力扣解题记录--第136题(查找单数)
  • Springboot+Layui英语单词学习系统的设计与实现
  • MyBatis Plus 分页
  • WiFi Mouse PC端 v1.7.2 官方中文版
  • 《杜甫传》读书笔记与经典摘要(三)流亡与走向人民
  • SPSC无锁环形队列技术(C++)
  • 系统整理Python的循环语句和常用方法
  • CPA青少年编程能力等级测评试卷及答案 Python编程(三级)
  • 详解力扣高频SQL50题之610. 判断三角形【简单】
  • 内存泄漏问题排查
  • idea打开后project窗口未显示项目名称的解决方案