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

网站建设营销怎么做怎样做电商 入手

网站建设营销怎么做,怎样做电商 入手,做杂志的网站有哪些,新建站点目录 前言 什么是定时器 如何使用java中的定时器 实现计时器 实现MyTimeTask类 Time类中存储任务的数据结构 实现Timer中的schedule方法 实现MyTimer中的构造方法 处理构造方法中出现的线程安全问题 完整代码 考虑在限时等待wait中能否用sleep替换 能否用PriorityBlo…

目录

前言

什么是定时器

如何使用java中的定时器

实现计时器

实现MyTimeTask类

Time类中存储任务的数据结构

实现Timer中的schedule方法

 实现MyTimer中的构造方法

处理构造方法中出现的线程安全问题

完整代码

 考虑在限时等待wait中能否用sleep替换

能否用PriorityBlockingQueue进行存储


在前面,已经讲解了几种常见设计模式,那么今天我们就来讲解一下定时器。

前言

在发送信息的时候,有时候不想要信息那么快就发送出去,而是在特定的时间再发送;或者我们在发送邮件时,当达到特定的时间时,就会自动发送电子邮件给用户,那么这里就需要用到定时器,那么定时器是什么呢?

什么是定时器

定时器是软件开发中的一个重要组件,类似于“闹钟”,能够在某个特定的时间执行一个或者多个任务,定时器是多线程中的一个案例,也是一个比较复杂且重要的案例。

如何使用java中的定时器

在java中,给我们提供了实现了的定时器包,我们可以直接使用。

java中给我们提供的定时器是Timer,我们在设置定时任务时,需要用到其中的schedule方法。

schedule包含两个参数:

第⼀个参数指定即将要执⾏的任务代码

第⼆个参数指定多⻓时间之后 执⾏(单位为毫秒)

示例:

class Demos{public static void main(String[] args) {// 创建一个Timer对象,用于调度定时任务Timer timer=new Timer();// 调度第一个定时任务,1秒后执行timer.schedule(new TimerTask() {@Overridepublic void run() {System.out.println("hello");}},1000);// 调度第二个定时任务,2秒后执行timer.schedule(new TimerTask() {@Overridepublic void run() {System.out.println("hello");}},2000);// 调度第三个定时任务,3秒后执行timer.schedule(new TimerTask() {@Overridepublic void run() {System.out.println("hello");}},3000);}}

实现计时器

我们从上述代码中可以看出,要实现一个定时器,需要实现以下:

  1. 实现一个任务(Task)类
  2. 实现一个Timer类用来存放任务

实现MyTimeTask类

/*** TimeTask类用于封装一个延迟执行的任务* 它包含一个需要执行的任务和一个延迟时间*/
class TimeTask{//需要执行的任务private Runnable runnable;//任务等待执行的时间点,以毫秒为单位private long time;/*** 构造函数,用于创建一个TimeTask对象* @param runnable 需要延迟执行的任务,类型为Runnable* @param delay    任务延迟执行的时间,单位为毫秒*/public TimeTask(Runnable runnable,long  delay){this.runnable=runnable;//计算任务应该执行的时间点this.time=System.currentTimeMillis()+delay;}/*** 返回任务的等待执行时间* @return 任务等待执行的时间点,以毫秒为单位*/public long getTime(){return this.time;}/*** 执行当前任务* 调用构造时传入的Runnable对象的run方法来执行任务*/public void run(){this.runnable.run();}
}

Time类中存储任务的数据结构

我们在存储任务的时候,需要根据等待时间来存储,时间短的优先取出来,那么我们就可以使用优先级队列,创建一个小根堆,时间最短的放在堆顶。

    private PriorityQueue<MyTimeTask> pq=new PriorityQueue<>();

但是我们这里要怎么比较呢?我们可以实现Comparable接口重写compareTo方法来进行比较,或者创建一个类来实现Comparator接口重写compare方法来进行比较。

实现Timer中的schedule方法

当我们解决了在任务在优先级队列中如何进行比较存储任务的问题之后,那么就可以在MyTimer中来实现schedule方法。根据schedule方法的参数创建一个MyTimeTask类,并将其添加到优先级队列中。

/*** 将一个 Runnable 任务安排在指定的延迟时间后执行* * @param runnable 要执行的任务* @param delay    相对于现在的时间延迟,单位为毫秒* * 注意:这个方法使用一个优先队列(pq)来管理这些被安排的任务,确保它们在指定的延迟后被执行*/
public void schedule(Runnable runnable, long delay) {// 创建一个 MyTimeTask 对象,它包含了 Runnable 任务和延迟时间MyTimeTask myTimeTask = new MyTimeTask(runnable, delay);// 将 MyTimeTask 对象添加到优先队列 pq 中,以便在未来的某个时间执行pq.add(myTimeTask);
}

 实现MyTimer中的构造方法

通过实例化一个线程,在这个线程中,通过多次扫描优先级队列中的元素,判断堆顶元素是否到达了等待时长,若是,则取出并执行。

注意:这里不能直接poll取出栈顶元素,若栈顶的任务等待时间还未到达,则继续循环。

 /*** 构造函数,初始化MyTimer对象* 创建并启动一个线程,用于持续检查并执行已到达设定时间的任务*/public MyTimer(){// 创建一个新的线程来执行定时任务Thread t=new Thread(()->{// 无限循环,持续检查任务队列while(true) {if(pq.isEmpty()){continue;}// 查看在栈顶的任务MyTimeTask task = pq.peek();// 比较栈顶的任务与当前时间点的比较if (System.currentTimeMillis() >= task.getTime()) {// 当前时间已达到任务设定时间,移除任务并执行pq.poll();task.run();}else {// 如果栈顶的任务时间大于当前时间,则继续循环// 继续检查下一个任务,或在没有到达时间的任务时继续等待continue;}}});// 启动线程t.start();}

处理构造方法中出现的线程安全问题

在上述代码中,能看出哪里存在线程安全问题吗?

优先级队列并不是一个线程安全的队列,我们在瞥(peek)取(poll)的时候,可能会出现线程安全问题,若是在多线程环境中,当线程1刚peek了堆顶任务,但此时切换到线程2,线程2同样peek堆顶任务,并刚好到了等待时间,此时就会执行并且删除栈顶任务。此时又切换到线程1,但此时线程1peek的堆顶任务已经被poll掉了,此时如果再执行,就会再次删除堆顶任务,导致出现线程安全问题。

所以,这里我们需要对peek和poll操作进行加锁。

    /*** 构造函数,初始化MyTimer对象* 创建并启动一个线程,用于持续检查并执行已到达设定时间的任务*/public MyTimer(){// 创建一个新的线程来执行定时任务Thread t=new Thread(()->{// 无限循环,持续检查任务队列while(true) {synchronized (lock) {// 如果任务队列为空,则跳过当前循环if (pq.isEmpty()) {continue;}// 查看在栈顶的任务MyTimeTask task = pq.peek();// 比较栈顶的任务与当前时间点的比较if (System.currentTimeMillis() >= task.getTime()) {// 当前时间已达到任务设定时间,移除任务并执行pq.poll();task.run();} else {// 如果栈顶的任务时间大于当前时间,则继续循环// 继续检查下一个任务,或在没有到达时间的任务时继续等待continue;}}}});// 启动线程t.start();}

 这里还有什么能优化的吗?

我们可以看到,当优先级队列中不为空,但此时堆顶任务的等待时间还没到,此时就会进入else分支执行continue,但一直重复这样操作,可能会造成不断检查,cpu使用率过高。那么我们就可以使用带参数的wait来进行限时等待,当达到时限时,会自动唤醒线程。

同理的,在判断队列是否为空时,我们可以设置不带参数的wait,等待唤醒。

    /*** 构造函数,初始化MyTimer对象* 创建并启动一个线程,用于持续检查并执行已到达设定时间的任务*/public MyTimer(){// 创建一个新的线程来执行定时任务Thread t=new Thread(()->{// 无限循环,持续检查任务队列while(true) {synchronized (lock) {// 如果任务队列为空,则跳过当前循环while (pq.isEmpty()) {try {lock.wait();} catch (InterruptedException e) {throw new RuntimeException(e);}}// 查看在栈顶的任务MyTimeTask task = pq.peek();// 比较栈顶的任务与当前时间点的比较if (System.currentTimeMillis() >= task.getTime()) {// 当前时间已达到任务设定时间,移除任务并执行pq.poll();task.run();} else {// 如果栈顶的任务时间大于当前时间,则继续循环try {lock.wait(task.getTime()-System.currentTimeMillis());} catch (InterruptedException e) {throw new RuntimeException(e);}}}}});// 启动线程t.start();}

既然这里等待,那么我们就需要有人来唤醒wait,所以我们在schedule方法中也需要进行加锁,并且在添加完任务后,调用notify来进行通知。

/*** 将一个 Runnable 任务安排在指定的延迟时间后执行** @param runnable 要执行的任务* @param delay    相对于现在的时间延迟,单位为毫秒** 注意:这个方法使用一个优先队列(pq)来管理这些被安排的任务,确保它们在指定的延迟后被执行*/public void schedule(Runnable runnable, long delay) {synchronized (lock) {// 创建一个 MyTimeTask 对象,它包含了 Runnable 任务和延迟时间MyTimeTask myTimeTask = new MyTimeTask(runnable, delay);// 将 MyTimeTask 对象添加到优先队列 pq 中,以便在未来的某个时间执行pq.add(myTimeTask);// 唤醒可能在等待执行任务的线程lock.notify();}}

 MyTimer优化到这里,其实已经优化好了。

完整代码

package Threads;import java.util.Comparator;
import java.util.PriorityQueue;/*** TimeTask类用于封装一个延迟执行的任务* 它包含一个需要执行的任务和一个延迟时间*/
class TimeTask implements Comparable<TimeTask>{//需要执行的任务private Runnable runnable;//任务等待执行的时间点,以毫秒为单位private long time;/*** 构造函数,用于创建一个TimeTask对象* @param runnable 需要延迟执行的任务,类型为Runnable* @param delay    任务延迟执行的时间,单位为毫秒*/public TimeTask(Runnable runnable,long  delay){this.runnable=runnable;//计算任务应该执行的时间点this.time=System.currentTimeMillis()+delay;}/*** 返回任务的等待执行时间* @return 任务等待执行的时间点,以毫秒为单位*/public long getTime(){return this.time;}/*** 执行当前任务* 调用构造时传入的Runnable对象的run方法来执行任务*/public void run(){this.runnable.run();}@Overridepublic int compareTo(TimeTask o) {return (int) (this.time- o.getTime());}
}
class MyTimer{//private PriorityQueue<MyTimeTask> pq=new PriorityQueue<>(new compareTimeTask());private PriorityQueue<MyTimeTask> pq=new PriorityQueue<>();static Object lock=new Object();/*** 构造函数,初始化MyTimer对象* 创建并启动一个线程,用于持续检查并执行已到达设定时间的任务*/public MyTimer(){// 创建一个新的线程来执行定时任务Thread t=new Thread(()->{// 无限循环,持续检查任务队列while(true) {synchronized (lock) {// 如果任务队列为空,则跳过当前循环while (pq.isEmpty()) {try {lock.wait();} catch (InterruptedException e) {throw new RuntimeException(e);}}// 查看在栈顶的任务MyTimeTask task = pq.peek();// 比较栈顶的任务与当前时间点的比较if (System.currentTimeMillis() >= task.getTime()) {// 当前时间已达到任务设定时间,移除任务并执行pq.poll();task.run();} else {// 如果栈顶的任务时间大于当前时间,则继续循环try {lock.wait(task.getTime()-System.currentTimeMillis());} catch (InterruptedException e) {throw new RuntimeException(e);}}}}});// 启动线程t.start();}/*** 将一个 Runnable 任务安排在指定的延迟时间后执行** @param runnable 要执行的任务* @param delay    相对于现在的时间延迟,单位为毫秒** 注意:这个方法使用一个优先队列(pq)来管理这些被安排的任务,确保它们在指定的延迟后被执行*/public void schedule(Runnable runnable, long delay) {synchronized (lock) {// 创建一个 MyTimeTask 对象,它包含了 Runnable 任务和延迟时间MyTimeTask myTimeTask = new MyTimeTask(runnable, delay);// 将 MyTimeTask 对象添加到优先队列 pq 中,以便在未来的某个时间执行pq.add(myTimeTask);// 唤醒可能在等待执行任务的线程lock.notify();}}}
class Demo{public static void main(String[] args) {MyTimer timer=new MyTimer();timer.schedule(()->{System.out.println("Hello World1");},1000);timer.schedule(()->{System.out.println("Hello World2");},2000);timer.schedule(()->{System.out.println("Hello World3");},3000);}
}
class compareTimeTask implements Comparator<MyTimeTask>{@Overridepublic int compare(MyTimeTask o1, MyTimeTask o2) {return (int) (o1.getTime()-o2.getTime());}
}

测试一下

 考虑在限时等待wait中能否用sleep替换

 Thread.sleep(task.getTime()-System.currentTimeMillis());

 这里为什么不用sleep呢?

在前面线程安全问题中已经讲解了wait和sleep的区别,在这里,如果我们使用sleep,会导致拉着锁一起进入睡眠,导致其他线程拿不到锁对象,无法进行加锁。

这会导致我们想要调用schedule方法添加任务时,拿不到锁对象。

能否用PriorityBlockingQueue进行存储

在前面,我们用的是优先级队列PriorityBlockingQueue来存储任务,但如果我们用PriorityBlockingQueue呢?

如果我们使用PriorityBlockingQueue,那么我们的方法也需要改成take(取)和put(存),才能用阻塞等待的效果。

修改代码:

package Threads;import java.util.Comparator;
import java.util.PriorityQueue;
import java.util.concurrent.PriorityBlockingQueue;/*** TimeTask类用于封装一个延迟执行的任务* 它包含一个需要执行的任务和一个延迟时间*/
class TimeTask implements Comparable<TimeTask>{//需要执行的任务private Runnable runnable;//任务等待执行的时间点,以毫秒为单位private long time;/*** 构造函数,用于创建一个TimeTask对象* @param runnable 需要延迟执行的任务,类型为Runnable* @param delay    任务延迟执行的时间,单位为毫秒*/public TimeTask(Runnable runnable,long  delay){this.runnable=runnable;//计算任务应该执行的时间点this.time=System.currentTimeMillis()+delay;}/*** 返回任务的等待执行时间* @return 任务等待执行的时间点,以毫秒为单位*/public long getTime(){return this.time;}/*** 执行当前任务* 调用构造时传入的Runnable对象的run方法来执行任务*/public void run(){this.runnable.run();}@Overridepublic int compareTo(TimeTask o) {return (int) (this.time- o.getTime());}
}
class MyTimer{//private PriorityQueue<MyTimeTask> pq=new PriorityQueue<>(new compareTimeTask());private PriorityBlockingQueue<MyTimeTask> pq=new PriorityBlockingQueue<>(100);static Object lock=new Object();/*** 构造函数,初始化MyTimer对象* 创建并启动一个线程,用于持续检查并执行已到达设定时间的任务*/public MyTimer(){// 创建一个新的线程来执行定时任务Thread t=new Thread(()->{// 无限循环,持续检查任务队列while(true) {synchronized (lock) {// 如果任务队列为空,则跳过当前循环while (pq.isEmpty()) {try {lock.wait();} catch (InterruptedException e) {throw new RuntimeException(e);}}// 查看在栈顶的任务MyTimeTask task = null;try {task = pq.take();} catch (InterruptedException e) {throw new RuntimeException(e);}// 比较栈顶的任务与当前时间点的比较if (System.currentTimeMillis() >= task.getTime()) {// 当前时间已达到任务设定时间,移除任务并执行task.run();} else {// 如果栈顶的任务时间大于当前时间,则继续循环pq.put(task);try {lock.wait(task.getTime()-System.currentTimeMillis());} catch (InterruptedException e) {throw new RuntimeException(e);}}}}});// 启动线程t.start();}/*** 将一个 Runnable 任务安排在指定的延迟时间后执行** @param runnable 要执行的任务* @param delay    相对于现在的时间延迟,单位为毫秒** 注意:这个方法使用一个优先队列(pq)来管理这些被安排的任务,确保它们在指定的延迟后被执行*/public void schedule(Runnable runnable, long delay) {synchronized (lock) {// 创建一个 MyTimeTask 对象,它包含了 Runnable 任务和延迟时间MyTimeTask myTimeTask = new MyTimeTask(runnable, delay);// 将 MyTimeTask 对象添加到优先队列 pq 中,以便在未来的某个时间执行pq.put(myTimeTask);// 唤醒可能在等待执行任务的线程lock.notify();}}}
class Demo{public static void main(String[] args) {MyTimer timer=new MyTimer();timer.schedule(()->{System.out.println("Hello World1");},1000);timer.schedule(()->{System.out.println("Hello World2");},2000);timer.schedule(()->{System.out.println("Hello World3");},3000);}
}
class compareTimeTask implements Comparator<MyTimeTask>{@Overridepublic int compare(MyTimeTask o1, MyTimeTask o2) {return (int) (o1.getTime()-o2.getTime());}
}

由于take会触发阻塞等待,而后面的wait也会,这里加了两次锁,容易引出线程安全问题,所以我们建议使用一个锁对象lock,来进行加锁就行。而不使用无界阻塞队列。


以上就是本篇所有内容~

若有不足,欢迎指正~

http://www.ds6.com.cn/news/123041.html

相关文章:

  • 阿里云香港虚拟主机seo优化顾问
  • 网站程序定制seo搜索方法
  • 专注与开发网站的北京网络公司成品短视频软件大全下载手机版
  • 郑州网站建设e橙网熊掌号google服务框架
  • 我想阻止一个网站要怎么做百度app下载并安装最新版
  • .net做网站用什么框架网络推广优化品牌公司
  • ps怎么做网站图片网站推广的方法有哪些
  • 苏州知名网站建设设计公司排名搜索大全引擎入口网站
  • 怎么判断网站有没有做百度商桥cba最新消息
  • 网站页面如何架构国际军事形势最新消息
  • 江门网站开发网站seo报价
  • 教育与培训网站建设网址导航下载到桌面
  • 何如做外贸网站推网免费的seo优化
  • 网站建设企业排名推广怎么搜索网站
  • 美女做爰直播在线网站网络营销软件
  • 在门户网站上爆光怎么做免费跨国浏览器
  • 免费手机网站建站2023年的新闻十条
  • 天津百度网站快速优化如何进行搜索引擎优化
  • 河间市做网站价格厦门谷歌seo公司
  • 黄色色调 网站网站域名在哪里查询
  • 有什么网站可以兼职做翻译专门代写平台
  • WordPress幻灯片首页抖音seo优化排名
  • 平度168网站建设百度自动搜索关键词软件
  • 网站做推广应该如何来做呢哪里推广优化大师是什么软件
  • 潍坊网站制作套餐网站seo外链平台
  • 自助建站系统厂家公司网络搭建
  • 临潼区做网站的公司网站优化外包价格
  • wordpress相册打造的视频弹出seo长沙
  • 合肥微信网站制作关键词优化最好的方法
  • 网站宣传的优点网站推广的作用在哪里