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分钟内没有付款,就自动取消订单. 短 ...
随机推荐
- Java 基本语法,标识符,修饰符,关键字
基本语法 编写 Java 程序时,应注意以下几点: 大小写敏感:Java 是大小写敏感的,这就意味着标识符 Hello 与 hello 是不同的. 类名:对于所有的类来说,类名的首字母应该大写.如果类 ...
- Django models文件模型变更注意事项(表结构的修改)
表结构的修改 1.表结构修改后,原来表中已存在的数据,就会出现结构混乱,makemigrations更新表的时候就会出错 比如第一次建模型,漏了一个字段,后来补上了.(经常遇到模型字段修改) 重新ma ...
- .net core 部署在iis上
1. 启用iis (过程略) 参考操作流程 2. 下载并安装 .NET Core Windows Server Hosting 下载链接 下载完成后安装 3. 启动 windows process a ...
- Linux密码重置
在启动菜单选择启动内核: 按e编辑,编辑修改两处:ro改为rw,和找到rhgb quiet一行: 把rhgb quiet替换为init=/bin/bash(临时生效): 按CTRL+X进入单用户模式: ...
- RabbitMQ 声明队列时候的参数解释
参考链接:http://www.rabbitmq.com/releases/rabbitmq-dotnet-client/v3.6.6/rabbitmq-dotnet-client-3.6.6-cli ...
- RabbitMQ通过Exchange.headers属性代替routekey,x-match实现队列精准匹配
消费者: static void Main(string[] args) { ConnectionFactory factory = new ConnectionFactory() { HostNam ...
- kafka 客户端 producer 配置参数
属性 描述 类型 默认值 bootstrap.servers 用于建立与kafka集群的连接,这个list仅仅影响用于初始化的hosts,来发现全部的servers.格式:host1:port1,ho ...
- 2018-2019 20165319 网络对抗 Exp4 恶意代码分析
基础问题回答 1. 如果在工作中怀疑一台主机上有恶意代码,但只是猜想,所有想监控下系统一天天的到底在干些什么.请设计下你想监控的操作有哪些,用什么方法来监控 答:1.使用Windows自带的schta ...
- 个人总结ASP.NET必备面试题
1.你能解释下MVC的完整流程吗? 所有的终端用户请求被发送到控制器.控制器依赖请求去选择加载哪个模型,并把模型附加到对应的视图.附加了模型数据的最终视图做为响应发送给终端用户. 2. 那你说一下你对 ...
- DCDC设计指南1
DC/DC电源设计指导:一 在设计电源模块的时候,第一时间要把该电源IC的datasheet资料下载好,查看里面的说明: 下面以一款DC/DC转换IC为例: 开始布局前先看下IC的特性说明,图1: 图 ...