Java 延迟队列使用
延时队列,第一他是个队列,所以具有对列功能第二就是延时,这就是延时对列,功能也就是将任务放在该延时对列中,只有到了延时时刻才能从该延时对列中获取任务否则获取不到……
应用场景比较多,比如延时1分钟发短信,延时1分钟再次执行等,下面先看看延时队列demo之后再看延时队列在项目中的使用:
简单的延时队列要有三部分:第一实现了Delayed接口的消息体、第二消费消息的消费者、第三存放消息的延时队列,那下面就来看看延时队列demo。
一、消息体
- package com.delqueue;
- import java.util.concurrent.Delayed;
- import java.util.concurrent.TimeUnit;
- /**
- * 消息体定义 实现Delayed接口就是实现两个方法即compareTo 和 getDelay最重要的就是getDelay方法,这个方法用来判断是否到期……
- *
- * @author whd
- * @date 2017年9月24日 下午8:57:14
- */
- public class Message implements Delayed {
- private int id;
- private String body; // 消息内容
- private long excuteTime;// 延迟时长,这个是必须的属性因为要按照这个判断延时时长。
- public int getId() {
- return id;
- }
- public String getBody() {
- return body;
- }
- public long getExcuteTime() {
- return excuteTime;
- }
- public Message(int id, String body, long delayTime) {
- this.id = id;
- this.body = body;
- this.excuteTime = TimeUnit.NANOSECONDS.convert(delayTime, TimeUnit.MILLISECONDS) + System.nanoTime();
- }
- // 自定义实现比较方法返回 1 0 -1三个参数
- @Override
- public int compareTo(Delayed delayed) {
- Message msg = (Message) delayed;
- return Integer.valueOf(this.id) > Integer.valueOf(msg.id) ? 1
- : (Integer.valueOf(this.id) < Integer.valueOf(msg.id) ? -1 : 0);
- }
- // 延迟任务是否到时就是按照这个方法判断如果返回的是负数则说明到期否则还没到期
- @Override
- public long getDelay(TimeUnit unit) {
- return unit.convert(this.excuteTime - System.nanoTime(), TimeUnit.NANOSECONDS);
- }
- }
二、消息消费者
- package com.delqueue;
- import java.util.concurrent.DelayQueue;
- public class Consumer implements Runnable {
- // 延时队列 ,消费者从其中获取消息进行消费
- private DelayQueue<Message> queue;
- public Consumer(DelayQueue<Message> queue) {
- this.queue = queue;
- }
- @Override
- public void run() {
- while (true) {
- try {
- Message take = queue.take();
- System.out.println("消费消息id:" + take.getId() + " 消息体:" + take.getBody());
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- }
- }
三、延时队列
- package com.delqueue;
- import java.util.concurrent.DelayQueue;
- import java.util.concurrent.ExecutorService;
- import java.util.concurrent.Executors;
- public class DelayQueueTest {
- public static void main(String[] args) {
- // 创建延时队列
- DelayQueue<Message> queue = new DelayQueue<Message>();
- // 添加延时消息,m1 延时3s
- Message m1 = new Message(1, "world", 3000);
- // 添加延时消息,m2 延时10s
- Message m2 = new Message(2, "hello", 10000);
- //将延时消息放到延时队列中
- queue.offer(m2);
- queue.offer(m1);
- // 启动消费线程 消费添加到延时队列中的消息,前提是任务到了延期时间
- ExecutorService exec = Executors.newFixedThreadPool(1);
- exec.execute(new Consumer(queue));
- exec.shutdown();
- }
- }
将消息体放入延迟队列中,在启动消费者线程去消费延迟队列中的消息,如果延迟队列中的消息到了延迟时间则可以从中取出消息否则无法取出消息也就无法消费。
这就是延迟队列demo,下面我们来说说在真实环境下的使用。
使用场景描述:
在打车软件中对订单进行派单的流程,当有订单的时候给该订单筛选司机,然后给当订单绑定司机,但是有时运气没那么好,订单进来后第一次没有筛选到合适的司机,但我们也不能就此结束派单,而是将该订单的信息放到延时队列中过个2秒钟在进行一次,其实这个2秒钟就是一个延迟,所以这里我们就可以使用延时队列来实现……
下面看看简单的流程图:
下面来看看具体代码实现:
在项目中有如下几个类:第一 、任务类 第二、按照任务类组装的消息体类 第三、延迟队列管理类
任务类即执行筛选司机、绑单、push消息的任务类
- package com.test.delayqueue;
- /**
- * 具体执行相关业务的业务类
- * @author whd
- * @date 2017年9月25日 上午12:49:32
- */
- public class DelayOrderWorker implements Runnable {
- @Override
- public void run() {
- // TODO Auto-generated method stub
- //相关业务逻辑处理
- System.out.println(Thread.currentThread().getName()+" do something ……");
- }
- }
消息体类,在延时队列中这个实现了Delayed接口的消息类是比不可少的,实现接口时有一个getDelay(TimeUnit unit)方法,这个方法就是判断是否到期的
这里定义的是一个泛型类,所以可以将我们上面的任务类作为其中的task,这样就将任务类分装成了一个消息体
- package com.test.delayqueue;
- import java.util.concurrent.Delayed;
- import java.util.concurrent.TimeUnit;
- /**
- * 延时队列中的消息体将任务封装为消息体
- *
- * @author whd
- * @date 2017年9月25日 上午12:48:30
- * @param <T>
- */
- public class DelayOrderTask<T extends Runnable> implements Delayed {
- private final long time;
- private final T task; // 任务类,也就是之前定义的任务类
- /**
- * @param timeout
- * 超时时间(秒)
- * @param task
- * 任务
- */
- public DelayOrderTask(long timeout, T task) {
- this.time = System.nanoTime() + timeout;
- this.task = task;
- }
- @Override
- public int compareTo(Delayed o) {
- // TODO Auto-generated method stub
- DelayOrderTask other = (DelayOrderTask) o;
- long diff = time - other.time;
- if (diff > 0) {
- return 1;
- } else if (diff < 0) {
- return -1;
- } else {
- return 0;
- }
- }
- @Override
- public long getDelay(TimeUnit unit) {
- // TODO Auto-generated method stub
- return unit.convert(this.time - System.nanoTime(), TimeUnit.NANOSECONDS);
- }
- @Override
- public int hashCode() {
- return task.hashCode();
- }
- public T getTask() {
- return task;
- }
- }
延时队列管理类,这个类主要就是将任务类封装成消息并并添加到延时队列中,以及轮询延时队列从中取出到时的消息体,在获取任务类放到线程池中执行任务
- package com.test.delayqueue;
- import java.util.Map;
- import java.util.concurrent.DelayQueue;
- import java.util.concurrent.ExecutorService;
- import java.util.concurrent.Executors;
- import java.util.concurrent.TimeUnit;
- import java.util.concurrent.atomic.AtomicLong;
- /**
- * 延时队列管理类,用来添加任务、执行任务
- *
- * @author whd
- * @date 2017年9月25日 上午12:44:59
- */
- public class DelayOrderQueueManager {
- private final static int DEFAULT_THREAD_NUM = 5;
- private static int thread_num = DEFAULT_THREAD_NUM;
- // 固定大小线程池
- private ExecutorService executor;
- // 守护线程
- private Thread daemonThread;
- // 延时队列
- private DelayQueue<DelayOrderTask<?>> delayQueue;
- private static final AtomicLong atomic = new AtomicLong(0);
- private final long n = 1;
- private static DelayOrderQueueManager instance = new DelayOrderQueueManager();
- private DelayOrderQueueManager() {
- executor = Executors.newFixedThreadPool(thread_num);
- delayQueue = new DelayQueue<>();
- init();
- }
- public static DelayOrderQueueManager getInstance() {
- return instance;
- }
- /**
- * 初始化
- */
- public void init() {
- daemonThread = new Thread(() -> {
- execute();
- });
- daemonThread.setName("DelayQueueMonitor");
- daemonThread.start();
- }
- private void execute() {
- while (true) {
- Map<Thread, StackTraceElement[]> map = Thread.getAllStackTraces();
- System.out.println("当前存活线程数量:" + map.size());
- int taskNum = delayQueue.size();
- System.out.println("当前延时任务数量:" + taskNum);
- try {
- // 从延时队列中获取任务
- DelayOrderTask<?> delayOrderTask = delayQueue.take();
- if (delayOrderTask != null) {
- Runnable task = delayOrderTask.getTask();
- if (null == task) {
- continue;
- }
- // 提交到线程池执行task
- executor.execute(task);
- }
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- }
- /**
- * 添加任务
- *
- * @param task
- * @param time
- * 延时时间
- * @param unit
- * 时间单位
- */
- public void put(Runnable task, long time, TimeUnit unit) {
- // 获取延时时间
- long timeout = TimeUnit.NANOSECONDS.convert(time, unit);
- // 将任务封装成实现Delayed接口的消息体
- DelayOrderTask<?> delayOrder = new DelayOrderTask<>(timeout, task);
- // 将消息体放到延时队列中
- delayQueue.put(delayOrder);
- }
- /**
- * 删除任务
- *
- * @param task
- * @return
- */
- public boolean removeTask(DelayOrderTask task) {
- return delayQueue.remove(task);
- }
- }
测试类
- package com.delqueue;
- import java.util.concurrent.TimeUnit;
- import com.test.delayqueue.DelayOrderQueueManager;
- import com.test.delayqueue.DelayOrderWorker;
- public class Test {
- public static void main(String[] args) {
- DelayOrderWorker work1 = new DelayOrderWorker();// 任务1
- DelayOrderWorker work2 = new DelayOrderWorker();// 任务2
- DelayOrderWorker work3 = new DelayOrderWorker();// 任务3
- // 延迟队列管理类,将任务转化消息体并将消息体放入延迟对列中等待执行
- DelayOrderQueueManager manager = DelayOrderQueueManager.getInstance();
- manager.put(work1, 3000, TimeUnit.MILLISECONDS);
- manager.put(work2, 6000, TimeUnit.MILLISECONDS);
- manager.put(work3, 9000, TimeUnit.MILLISECONDS);
- }
- }
OK 这就是项目中的具体使用情况,当然具体内容被忽略,整体框架就是这样,还有这里使用java的延时队列但是这种方式是有问题的如果如果down机则会出现任务丢失,所以也可以考虑使用mq、redis来实现
Java 延迟队列使用的更多相关文章
- java延迟队列
大多数用到定时执行的功能都是用任务调度来做的,单身当碰到类似订餐业务/购物等这种业务就不好处理了,比如购物的订单功能,在你的订单管理中有N个订单,当订单超过十分钟未支付的时候自动释放购物车中的商品,订 ...
- Spring Boot(十四)RabbitMQ延迟队列
一.前言 延迟队列的使用场景:1.未按时支付的订单,30分钟过期之后取消订单:2.给活跃度比较低的用户间隔N天之后推送消息,提高活跃度:3.过1分钟给新注册会员的用户,发送注册邮件等. 实现延迟队列的 ...
- 使用netty HashedWheelTimer构建简单延迟队列
背景 最近项目中有个业务,需要对用户新增任务到期后进行业务处理.使用定时任务定时扫描过期时间,浪费资源,且不实时.只能使用延时队列处理. DelayQueue 第一想到的是java自带的延时队列del ...
- rabbitmq延迟队列demo
1. demo详解 1.1 工程结构: 1.2 pom 定义jar包依赖的版本.版本很重要,rabbit依赖spring,两者必须相一致,否则报错: <properties> <sp ...
- JUC——延迟队列
所谓的延迟队列最大的特征是它可以自动通过队列进行脱离,例如:现在有一些对象被临时保存着,但是有可能该集合对象是一个公共对象,那么里面的某些数据如果不在使用的时候就希望其可以在指定的时间达到后自动的消失 ...
- Java阻塞队列(BlockingQueue)实现 生产者/消费者 示例
Java阻塞队列(BlockingQueue)实现 生产者/消费者 示例 本文由 TonySpark 翻译自 Javarevisited.转载请参见文章末尾的要求. Java.util.concurr ...
- RabbitMQ 延迟队列,消息延迟推送
目录 应用场景 消息延迟推送的实现 测试结果 应用场景 目前常见的应用软件都有消息的延迟推送的影子,应用也极为广泛,例如: 淘宝七天自动确认收货.在我们签收商品后,物流系统会在七天后延时发送一个消息给 ...
- Redis(二)延迟队列
1.目录 延迟队列 进一步优化 2.延迟队列 package com.redis; import java.lang.reflect.Type; import java.util.Set; impor ...
- Spring Boot (26) RabbitMQ延迟队列
延迟消息就是指当消息被发送以后,并不想让消费者立即拿到消息,而是等待指定时间后,消费者才拿到这个消息进行消费. 延迟队列 订单业务: 在电商/点餐中,都有下单后30分钟内没有付款,就自动取消订单. 短 ...
随机推荐
- W3CSchool实战闯关笔记(JavaScript)
//handsome /** *ugly **/ 第一关注释 // 举例 var myName; // Define myName below this line 第二关声明变量 // Setup v ...
- 使用Open Live Write发布CSDN博客
---安装open live write 1.序 在CSDN上发布博客相当麻烦,图片一张张的上传确实让人头大,虽然通过office也能发布博客,不过Open Live Write软件使用感觉更好. 2 ...
- IE8引用jQuery报$或者jQuery未定义
最近公司做的项目要求兼容到IE8,结果在页面调试的时候出了个bug,在IE8上面一直报错$未定义,或者jQuery未定义,导致页面上面写的jQuery全部失效,在Chrome浏览器没有任何问题.很是头 ...
- mysqldump命令使用
1.执行mysqldump命令前,先给配制添加帐号和密码: # vi /etc/my.cnf user=root password=XXXXXX 2. # mysqldump -uroot -pXXX ...
- WPF使用CefSharp嵌入网页
1.点击项目应用下的管理NuGet程序包 2.在浏览中输入cefsharp-->查找 CefSharp.Wpf-->点击安装,等待安装完成 3.如果遇到一下问题将解决方案和项目都改成64位 ...
- mysql5.7初始化密码报错 ERROR 1820 (HY000): You must reset your password using ALTER USER statement before
mysql初始化密码常见报错问题1,mysql5.6是密码为空直接进入数据库的,但是mysql5.7就需要初始密码 cat /var/log/mysqld.log | grep password1 2 ...
- nginx 10054报错问题解决方案
使用nginx代理,端口8000.tomcat用于后端服务器,端口8080.nginx的error.log中报如下错误: 2018/09/21 09:08:06 [error] 12488#11600 ...
- 理解WindowManagerService
--摘自<Android进阶解密> *WMS的职责* 1)窗口管理 WMS负责窗口的启动.添加和删除,另外窗口的大小和层级也是由WMS进行管理的 2)窗口动画 WMS的动画子系统Windo ...
- spring-boot-starter-actouator2.1.4与c3p0版本0.9.1.2冲突
报错前的pom文件: <?xml version="1.0" encoding="UTF-8"?><project xmlns="h ...
- AngularJS 最常用的八种功能
转载地址:https://zhaoyanblog.com/archives/99.html 第一 迭代输出之ng-repeat标签ng-repeat让table ul ol等标签和js里的数组完美结合 ...