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分钟内没有付款,就自动取消订单. 短 ...
随机推荐
- jQuery时间轴插件timeline.js
http://www.jq22.com/jquery-info13695 http://www.jq22.com/jquery-info13357 简要教程 timeline.js是一款jQuery时 ...
- bat实现固定时间循环抓取设备log
背景:测试时需要实时抓取android设备log,但是一份log抓取过来非常庞大(有时超过500M+,编辑器都打不开,还得找工具进行分割,甚是蛋疼),查看也非常不方便. 解决:基于上述情况,与其之后进 ...
- python的pandas库学习笔记
导入: import pandas as pd from pandas import Series,DataFrame 1.两个主要数据结构:Series和DataFrame (1)Series是一种 ...
- php 获取用户的IP、地址、来源
js方法获取用户的 ip 和 地址 <script src="http://pv.sohu.com/cityjson?ie=utf-8"></script> ...
- JS中函数常见的表现形式以及立即执行函数
函数常见的几种表现形式: 1.一般形式(函数声明): 会进行函数的预解释,函数会进行声明和定义,在函数体前面或则后面都可以进行调用. 2.函数表达式(匿名函数): 会进行函数的预解析,函数会进行声明但 ...
- 第一阶段——CentOS6_Python3.6.1笔记(尚学堂-Python基础快速入门)+ 【补充】麦子-Python程序入门与进阶
虚拟机环境: 设置网络 .修改网络地址 .设置网卡为nat模式 .确保物理机启动dhcp.net服务 .编辑文件:vim /etc/sysconfig/network-scripts/ifcfg-et ...
- UWP Acrylic Material
文档:https://docs.microsoft.com/en-us/windows/uwp/design/style/acrylic Acrylic 能带来类似 win7 的毛玻璃效果 要使用 A ...
- kafka常规及几个重要的操作命令
1. 查看所有topic kafka-topics.sh --zookeeper hadoop3 --list 2. 创建tooic及topic的partitioner ./kafka-topics. ...
- 【JavaScript】$.extend使用心得及源码研究
最近写多了js的面向对象编程,用$.extend写继承写得很顺手.但是在使用过程中发现有几个问题. 1.深拷贝 $.extend默认是浅拷贝,这意味着在继承复杂对象时,对象中内嵌的对象无法被拷贝到. ...
- 利用阿里云搭建frp实现外网远程桌面链接内网电脑
主要应用场景:针对学生放假回家使用外网无法远程操作学校的服务器或者电脑,这里通过阿里云的云服务器搭建一个frp服务,实现内网穿透,从而可以直接通过远程桌面或者其他工具实现对校园网内的服务器或者电脑进行 ...