延时队列,第一他是个队列,所以具有对列功能第二就是延时,这就是延时对列,功能也就是将任务放在该延时对列中,只有到了延时时刻才能从该延时对列中获取任务否则获取不到……

应用场景比较多,比如延时1分钟发短信,延时1分钟再次执行等,下面先看看延时队列demo之后再看延时队列在项目中的使用:

简单的延时队列要有三部分:第一实现了Delayed接口的消息体、第二消费消息的消费者、第三存放消息的延时队列,那下面就来看看延时队列demo。

一、消息体

  1. package com.delqueue;
  2. import java.util.concurrent.Delayed;
  3. import java.util.concurrent.TimeUnit;
  4. /**
  5. * 消息体定义 实现Delayed接口就是实现两个方法即compareTo 和 getDelay最重要的就是getDelay方法,这个方法用来判断是否到期……
  6. *
  7. * @author whd
  8. * @date 2017年9月24日 下午8:57:14
  9. */
  10. public class Message implements Delayed {
  11. private int id;
  12. private String body; // 消息内容
  13. private long excuteTime;// 延迟时长,这个是必须的属性因为要按照这个判断延时时长。
  14. public int getId() {
  15. return id;
  16. }
  17. public String getBody() {
  18. return body;
  19. }
  20. public long getExcuteTime() {
  21. return excuteTime;
  22. }
  23. public Message(int id, String body, long delayTime) {
  24. this.id = id;
  25. this.body = body;
  26. this.excuteTime = TimeUnit.NANOSECONDS.convert(delayTime, TimeUnit.MILLISECONDS) + System.nanoTime();
  27. }
  28. // 自定义实现比较方法返回 1 0 -1三个参数
  29. @Override
  30. public int compareTo(Delayed delayed) {
  31. Message msg = (Message) delayed;
  32. return Integer.valueOf(this.id) > Integer.valueOf(msg.id) ? 1
  33. : (Integer.valueOf(this.id) < Integer.valueOf(msg.id) ? -1 : 0);
  34. }
  35. // 延迟任务是否到时就是按照这个方法判断如果返回的是负数则说明到期否则还没到期
  36. @Override
  37. public long getDelay(TimeUnit unit) {
  38. return unit.convert(this.excuteTime - System.nanoTime(), TimeUnit.NANOSECONDS);
  39. }
  40. }

二、消息消费者

  1. package com.delqueue;
  2. import java.util.concurrent.DelayQueue;
  3. public class Consumer implements Runnable {
  4. // 延时队列 ,消费者从其中获取消息进行消费
  5. private DelayQueue<Message> queue;
  6. public Consumer(DelayQueue<Message> queue) {
  7. this.queue = queue;
  8. }
  9. @Override
  10. public void run() {
  11. while (true) {
  12. try {
  13. Message take = queue.take();
  14. System.out.println("消费消息id:" + take.getId() + " 消息体:" + take.getBody());
  15. } catch (InterruptedException e) {
  16. e.printStackTrace();
  17. }
  18. }
  19. }
  20. }

三、延时队列

  1. package com.delqueue;
  2. import java.util.concurrent.DelayQueue;
  3. import java.util.concurrent.ExecutorService;
  4. import java.util.concurrent.Executors;
  5. public class DelayQueueTest {
  6. public static void main(String[] args) {
  7. // 创建延时队列
  8. DelayQueue<Message> queue = new DelayQueue<Message>();
  9. // 添加延时消息,m1 延时3s
  10. Message m1 = new Message(1, "world", 3000);
  11. // 添加延时消息,m2 延时10s
  12. Message m2 = new Message(2, "hello", 10000);
  13. //将延时消息放到延时队列中
  14. queue.offer(m2);
  15. queue.offer(m1);
  16. // 启动消费线程 消费添加到延时队列中的消息,前提是任务到了延期时间
  17. ExecutorService exec = Executors.newFixedThreadPool(1);
  18. exec.execute(new Consumer(queue));
  19. exec.shutdown();
  20. }
  21. }

将消息体放入延迟队列中,在启动消费者线程去消费延迟队列中的消息,如果延迟队列中的消息到了延迟时间则可以从中取出消息否则无法取出消息也就无法消费。

这就是延迟队列demo,下面我们来说说在真实环境下的使用。

使用场景描述:

在打车软件中对订单进行派单的流程,当有订单的时候给该订单筛选司机,然后给当订单绑定司机,但是有时运气没那么好,订单进来后第一次没有筛选到合适的司机,但我们也不能就此结束派单,而是将该订单的信息放到延时队列中过个2秒钟在进行一次,其实这个2秒钟就是一个延迟,所以这里我们就可以使用延时队列来实现……

下面看看简单的流程图:

下面来看看具体代码实现:

在项目中有如下几个类:第一 、任务类   第二、按照任务类组装的消息体类  第三、延迟队列管理类

任务类即执行筛选司机、绑单、push消息的任务类

  1. package com.test.delayqueue;
  2. /**
  3. * 具体执行相关业务的业务类
  4. * @author whd
  5. * @date 2017年9月25日 上午12:49:32
  6. */
  7. public class DelayOrderWorker  implements Runnable {
  8. @Override
  9. public void run() {
  10. // TODO Auto-generated method stub
  11. //相关业务逻辑处理
  12. System.out.println(Thread.currentThread().getName()+" do something ……");
  13. }
  14. }

消息体类,在延时队列中这个实现了Delayed接口的消息类是比不可少的,实现接口时有一个getDelay(TimeUnit unit)方法,这个方法就是判断是否到期的

这里定义的是一个泛型类,所以可以将我们上面的任务类作为其中的task,这样就将任务类分装成了一个消息体

  1. package com.test.delayqueue;
  2. import java.util.concurrent.Delayed;
  3. import java.util.concurrent.TimeUnit;
  4. /**
  5. * 延时队列中的消息体将任务封装为消息体
  6. *
  7. * @author whd
  8. * @date 2017年9月25日 上午12:48:30
  9. * @param <T>
  10. */
  11. public class DelayOrderTask<T extends Runnable> implements Delayed {
  12. private final long time;
  13. private final T task; // 任务类,也就是之前定义的任务类
  14. /**
  15. * @param timeout
  16. *            超时时间(秒)
  17. * @param task
  18. *            任务
  19. */
  20. public DelayOrderTask(long timeout, T task) {
  21. this.time = System.nanoTime() + timeout;
  22. this.task = task;
  23. }
  24. @Override
  25. public int compareTo(Delayed o) {
  26. // TODO Auto-generated method stub
  27. DelayOrderTask other = (DelayOrderTask) o;
  28. long diff = time - other.time;
  29. if (diff > 0) {
  30. return 1;
  31. } else if (diff < 0) {
  32. return -1;
  33. } else {
  34. return 0;
  35. }
  36. }
  37. @Override
  38. public long getDelay(TimeUnit unit) {
  39. // TODO Auto-generated method stub
  40. return unit.convert(this.time - System.nanoTime(), TimeUnit.NANOSECONDS);
  41. }
  42. @Override
  43. public int hashCode() {
  44. return task.hashCode();
  45. }
  46. public T getTask() {
  47. return task;
  48. }
  49. }

延时队列管理类,这个类主要就是将任务类封装成消息并并添加到延时队列中,以及轮询延时队列从中取出到时的消息体,在获取任务类放到线程池中执行任务

  1. package com.test.delayqueue;
  2. import java.util.Map;
  3. import java.util.concurrent.DelayQueue;
  4. import java.util.concurrent.ExecutorService;
  5. import java.util.concurrent.Executors;
  6. import java.util.concurrent.TimeUnit;
  7. import java.util.concurrent.atomic.AtomicLong;
  8. /**
  9. * 延时队列管理类,用来添加任务、执行任务
  10. *
  11. * @author whd
  12. * @date 2017年9月25日 上午12:44:59
  13. */
  14. public class DelayOrderQueueManager {
  15. private final static int DEFAULT_THREAD_NUM = 5;
  16. private static int thread_num = DEFAULT_THREAD_NUM;
  17. // 固定大小线程池
  18. private ExecutorService executor;
  19. // 守护线程
  20. private Thread daemonThread;
  21. // 延时队列
  22. private DelayQueue<DelayOrderTask<?>> delayQueue;
  23. private static final AtomicLong atomic = new AtomicLong(0);
  24. private final long n = 1;
  25. private static DelayOrderQueueManager instance = new DelayOrderQueueManager();
  26. private DelayOrderQueueManager() {
  27. executor = Executors.newFixedThreadPool(thread_num);
  28. delayQueue = new DelayQueue<>();
  29. init();
  30. }
  31. public static DelayOrderQueueManager getInstance() {
  32. return instance;
  33. }
  34. /**
  35. * 初始化
  36. */
  37. public void init() {
  38. daemonThread = new Thread(() -> {
  39. execute();
  40. });
  41. daemonThread.setName("DelayQueueMonitor");
  42. daemonThread.start();
  43. }
  44. private void execute() {
  45. while (true) {
  46. Map<Thread, StackTraceElement[]> map = Thread.getAllStackTraces();
  47. System.out.println("当前存活线程数量:" + map.size());
  48. int taskNum = delayQueue.size();
  49. System.out.println("当前延时任务数量:" + taskNum);
  50. try {
  51. // 从延时队列中获取任务
  52. DelayOrderTask<?> delayOrderTask = delayQueue.take();
  53. if (delayOrderTask != null) {
  54. Runnable task = delayOrderTask.getTask();
  55. if (null == task) {
  56. continue;
  57. }
  58. // 提交到线程池执行task
  59. executor.execute(task);
  60. }
  61. } catch (Exception e) {
  62. e.printStackTrace();
  63. }
  64. }
  65. }
  66. /**
  67. * 添加任务
  68. *
  69. * @param task
  70. * @param time
  71. *            延时时间
  72. * @param unit
  73. *            时间单位
  74. */
  75. public void put(Runnable task, long time, TimeUnit unit) {
  76. // 获取延时时间
  77. long timeout = TimeUnit.NANOSECONDS.convert(time, unit);
  78. // 将任务封装成实现Delayed接口的消息体
  79. DelayOrderTask<?> delayOrder = new DelayOrderTask<>(timeout, task);
  80. // 将消息体放到延时队列中
  81. delayQueue.put(delayOrder);
  82. }
  83. /**
  84. * 删除任务
  85. *
  86. * @param task
  87. * @return
  88. */
  89. public boolean removeTask(DelayOrderTask task) {
  90. return delayQueue.remove(task);
  91. }
  92. }

测试类

  1. package com.delqueue;
  2. import java.util.concurrent.TimeUnit;
  3. import com.test.delayqueue.DelayOrderQueueManager;
  4. import com.test.delayqueue.DelayOrderWorker;
  5. public class Test {
  6. public static void main(String[] args) {
  7. DelayOrderWorker work1 = new DelayOrderWorker();// 任务1
  8. DelayOrderWorker work2 = new DelayOrderWorker();// 任务2
  9. DelayOrderWorker work3 = new DelayOrderWorker();// 任务3
  10. // 延迟队列管理类,将任务转化消息体并将消息体放入延迟对列中等待执行
  11. DelayOrderQueueManager manager = DelayOrderQueueManager.getInstance();
  12. manager.put(work1, 3000, TimeUnit.MILLISECONDS);
  13. manager.put(work2, 6000, TimeUnit.MILLISECONDS);
  14. manager.put(work3, 9000, TimeUnit.MILLISECONDS);
  15. }
  16. }

OK 这就是项目中的具体使用情况,当然具体内容被忽略,整体框架就是这样,还有这里使用java的延时队列但是这种方式是有问题的如果如果down机则会出现任务丢失,所以也可以考虑使用mq、redis来实现

Java 延迟队列使用的更多相关文章

  1. java延迟队列

    大多数用到定时执行的功能都是用任务调度来做的,单身当碰到类似订餐业务/购物等这种业务就不好处理了,比如购物的订单功能,在你的订单管理中有N个订单,当订单超过十分钟未支付的时候自动释放购物车中的商品,订 ...

  2. Spring Boot(十四)RabbitMQ延迟队列

    一.前言 延迟队列的使用场景:1.未按时支付的订单,30分钟过期之后取消订单:2.给活跃度比较低的用户间隔N天之后推送消息,提高活跃度:3.过1分钟给新注册会员的用户,发送注册邮件等. 实现延迟队列的 ...

  3. 使用netty HashedWheelTimer构建简单延迟队列

    背景 最近项目中有个业务,需要对用户新增任务到期后进行业务处理.使用定时任务定时扫描过期时间,浪费资源,且不实时.只能使用延时队列处理. DelayQueue 第一想到的是java自带的延时队列del ...

  4. rabbitmq延迟队列demo

    1. demo详解 1.1 工程结构: 1.2 pom 定义jar包依赖的版本.版本很重要,rabbit依赖spring,两者必须相一致,否则报错: <properties> <sp ...

  5. JUC——延迟队列

    所谓的延迟队列最大的特征是它可以自动通过队列进行脱离,例如:现在有一些对象被临时保存着,但是有可能该集合对象是一个公共对象,那么里面的某些数据如果不在使用的时候就希望其可以在指定的时间达到后自动的消失 ...

  6. Java阻塞队列(BlockingQueue)实现 生产者/消费者 示例

    Java阻塞队列(BlockingQueue)实现 生产者/消费者 示例 本文由 TonySpark 翻译自 Javarevisited.转载请参见文章末尾的要求. Java.util.concurr ...

  7. RabbitMQ 延迟队列,消息延迟推送

    目录 应用场景 消息延迟推送的实现 测试结果 应用场景 目前常见的应用软件都有消息的延迟推送的影子,应用也极为广泛,例如: 淘宝七天自动确认收货.在我们签收商品后,物流系统会在七天后延时发送一个消息给 ...

  8. Redis(二)延迟队列

    1.目录 延迟队列 进一步优化 2.延迟队列 package com.redis; import java.lang.reflect.Type; import java.util.Set; impor ...

  9. Spring Boot (26) RabbitMQ延迟队列

    延迟消息就是指当消息被发送以后,并不想让消费者立即拿到消息,而是等待指定时间后,消费者才拿到这个消息进行消费. 延迟队列 订单业务: 在电商/点餐中,都有下单后30分钟内没有付款,就自动取消订单. 短 ...

随机推荐

  1. C# EntityFramework Code First 迁移

    如果使用的是 Code First 工作流,推荐使用 Code First 迁移改进应用程序的数据库架构. 迁移提供一组允许以下操作的工具: 创建可用于 EF 模型的初始数据库 生成迁移以跟踪对 EF ...

  2. Java_File类

    File类以抽象的方式代表文件名和目录路径.该类主要用于文件和目录的创建.查找.删除等.先来看一下File的构造方法: // 通过将给定的路径名字符串转换为抽象路径名来创建新的 File实例 File ...

  3. SpringMVC是怎么工作的,SpringMVC的工作原理

    SpringWeb MVC 是怎么工作的,SpringMVC的原理,SpringMVC源码 分析. 介绍 SpringWeb MVC是Spring Framework中的一部分,当我们需要使用spri ...

  4. 【工具】Idea GenerateAllSetter

    使用工具自动生成setter方法调用,不是idea原生态生成getter/setter https://github.com/gejun123456/intellij-generateAllSetMe ...

  5. sql--迁移条件数据和补全数值的一个流程

    目的:我要把老顾客的部分数据迁移到另一个表里面 -- 步骤一:筛选查询-- 打开表,只显示想要看到的数据列-- 做条件筛选,筛选出想要的数据 -- 步骤二:sql查询 SELECT ID,Name,G ...

  6. sqlserver给用户配置存储过程查看权限

    对应的数据库->安全性->用户名右键属性-->安全对象-->指定所有对象-->选择服务器,里边有一个 查看定义(view any definition) 选项,勾上.

  7. hdfs 架构

    http://matt33.com/2018/07/15/hdfs-architecture-learn/

  8. 洛谷 p5020 货币系统 题解

    传送门 一个手动枚举能过一半点而且基本靠数学的题目(然而我考试的时候只有25分) 读清题目后发现就是凑数嘛,.... 对啊,就是凑数,怎么凑是重点啊.. 于是就绝望了一小时手动枚举n从1到5的情况 吐 ...

  9. Solr的配置和在java中的使用

    Solr是一个全局站内搜索引擎,可以快速的搜索出结果. Solr依赖于tomcat,把Solr的war包放到tomcat中即可运行. 使用solr,需要在solr的schema.xml中配置solr与 ...

  10. Ubuntu宿主机与VMware中其他系统虚拟机的互通

    Ubuntu做宿主机,VMware中创建Windows10,并且通过三种模式实现两系统互通,其实并非是件难事.在有线网卡未接网线的环境下,关闭两系统防火墙,基本遵从下文便可实现. 转载:https:/ ...