使用delayedQueue实现你本地的延迟队列

DelayQueue是一个无界阻塞队列,适用于需要延迟执行任务的场景。本文介绍了DelayQueue的概念,其在淘宝订单和饿了么订餐通知等业务中的应用,并详细说明如何创建Delayed对象,实现延迟任务管理类,以及具体使用方法,包括初始化容器和动态取消任务的策略。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

了解DelayQueue

DelayQueue是什么?

DelayQueue是一个无界的BlockingQueue,用于放置实现了Delayed接口的对象,其中的对象只能在其到期时才能从队列中取走。这种队列是有序的,即队头对象的延迟到期时间最长。注意:不能将null元素放置到这种队列中。

DelayQueue能做什么?

在我们的业务中通常会有一些需求是这样的:
1. 淘宝订单业务:下单之后如果三十分钟之内没有付款就自动取消订单
2. 饿了吗订餐通知:下单成功后60s之后给用户发送短信通知

那么这类业务我们可以总结出一个特点:需要延迟工作。
由此的情况,就是我们的DelayQueue应用需求的产生。

怎么用DelayQueue来解决这类的问题

先声明一个Delayed的对象


import java.util.concurrent.Delayed;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;

/**
 * <p>
 * [任务调度系统]
 * <br>
 * [队列中要执行的任务]
 * </p>
 *
 * @author wangguangdong
 * @version 1.0
 * @Date 2015年11月22日19:46:39
 */
public class Task<T extends Runnable> implements Delayed {
    /**
     * 到期时间
     */
    private final long time;

    /**
     * 问题对象
     */
    private final T task;
    private static final AtomicLong atomic = new AtomicLong(0);

    private final long n;

    public Task(long timeout, T t) {
        this.time = System.nanoTime() + timeout;
        this.task = t;
        this.n = atomic.getAndIncrement();
    }

    /**
     * 返回与此对象相关的剩余延迟时间,以给定的时间单位表示
     */
    @Override
    public long getDelay(TimeUnit unit) {
        return unit.convert(this.time - System.nanoTime(), TimeUnit.NANOSECONDS);
    }

    @Override
    public int compareTo(Delayed other) {
        // TODO Auto-generated method stub
        if (other == this) // compare zero ONLY if same object
            return 0;
        if (other instanceof Task) {
            Task x = (Task) other;
            long diff = time - x.time;
            if (diff < 0)
                return -1;
            else if (diff > 0)
                return 1;
            else if (n < x.n)
                return -1;
            else
                return 1;
        }
        long d = (getDelay(TimeUnit.NANOSECONDS) - other.getDelay(TimeUnit.NANOSECONDS));
        return (d == 0) ? 0 : ((d < 0) ? -1 : 1);
    }

    public T getTask() {
        return this.task;
    }

    @Override
    public int hashCode() {
        return task.hashCode();
    }

    @Override
    public boolean equals(Object object) {
        if (object instanceof Task) {
            return object.hashCode() == hashCode() ? true : false;
        }
        return false;
    }


}

再实现一个管理延迟任务的类

import org.apache.log4j.Logger;

import java.util.concurrent.DelayQueue;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

/**
 * <p>
 * [任务调度系统]
 * <br>
 * [后台守护线程不断的执行检测工作]
 * </p>
 *
 * @author wangguangdong
 * @version 1.0
 * @Date 2015年11月23日14:19:40
 */
public class TaskQueueDaemonThread {

    private static final Logger LOG = Logger.getLogger(TaskQueueDaemonThread.class);

    private TaskQueueDaemonThread() {
    }

    private static class LazyHolder {
        private static TaskQueueDaemonThread taskQueueDaemonThread = new TaskQueueDaemonThread();
    }

    public static TaskQueueDaemonThread getInstance() {
        return LazyHolder.taskQueueDaemonThread;
    }

    Executor executor = Executors.newFixedThreadPool(20);
    /**
     * 守护线程
     */
    private Thread daemonThread;

    /**
     * 初始化守护线程
     */
    public void init() {
        daemonThread = new Thread(() -> execute());
        daemonThread.setDaemon(true);
        daemonThread.setName("Task Queue Daemon Thread");
        daemonThread.start();
    }

    private void execute() {
        System.out.println("start:" + System.currentTimeMillis());
        while (true) {
            try {
                //从延迟队列中取值,如果没有对象过期则队列一直等待,
                Task t1 = t.take();
                if (t1 != null) {
                    //修改问题的状态
                    Runnable task = t1.getTask();
                    if (task == null) {
                        continue;
                    }
                    executor.execute(task);
                    LOG.info("[at task:" + task + "]   [Time:" + System.currentTimeMillis() + "]");
                }
            } catch (Exception e) {
                e.printStackTrace();
                break;
            }
        }
    }

    /**
     * 创建一个最初为空的新 DelayQueue
     */
    private DelayQueue<Task> t = new DelayQueue<>();

    /**
     * 添加任务,
     * time 延迟时间
     * task 任务
     * 用户为问题设置延迟时间
     */
    public void put(long time, Runnable task) {
        //转换成ns
        long nanoTime = TimeUnit.NANOSECONDS.convert(time, TimeUnit.MILLISECONDS);
        //创建一个任务
        Task k = new Task(nanoTime, task);
        //将任务放在延迟的队列中
        t.put(k);
    }

    /**
     * 结束订单
     * @param task
     */
    public boolean endTask(Task<Runnable> task){
        return t.remove(task);
    }
}

使用方法

  1. 在容器初始化的时候调用init方法.
  2. 实现一个runnable接口的类,调用TaskQueueDaemonThread的put方法传入进去.
  3. 如果需要实现动态的取消任务的话,需要task任务的类重新hashcode方法,最好用业务限制hashcode的冲突发生.
<think>好的,用户想了解如何使用Redission实现延迟队列的示例教程。我需要先回忆一下Redission的相关知识。记得Redission是一个基于Redis的Java客户端,提供了很多分布式服务,其中就包括延迟队列的功能。延迟队列通常用于需要延迟处理任务的场景,比如订单超时关闭、定时提醒等。 用户提供的引用中提到了两种方案,其中第二种是Redission实现延迟队列。引用[1]提到Redisson实现延迟队列是比较靠谱的方案,引用[2]给出了引入Redission的Maven依赖。所以,我需要结合这些信息来构建一个示例教程。 首先,我应该从配置Redission客户端开始,因为使用前需要连接到Redis服务器。配置部分可能需要创建一个Config对象,指定Redis服务器的地址和端口。然后初始化RedissonClient实例。 接下来,需要介绍如何创建延迟队列。Redission提供了RDelayedQueue接口,它需要绑定一个目标队列和一个延迟时间。用户需要先创建目标队列,比如使用RBlockingQueue,然后再创建RDelayedQueue,将目标队列和延迟时间关联起来。 然后,是生产者和消费者的实现。生产者使用延迟队列的offer方法添加元素,并指定延迟时间。消费者则从目标队列中获取元素,这里可能需要使用take方法,该方法会阻塞直到有元素可用。 还需要注意线程管理,尤其是在消费者端,可能需要启动一个单独的线程来持续监听队列。同时,异常处理和资源释放也是关键点,比如在程序结束时关闭Redission客户端,避免资源泄漏。 用户可能还关心实际的应用场景和注意事项,比如消息持久化、可靠性、性能等。这些应该在注意事项部分提到,确保用户了解如何在实际项目中正确使用。 最后,生成相关问题,帮助用户进一步学习,比如优化方法、异常处理、与其他方案的比较等。这些问题需要围绕Redission延迟队列实现细节和应用展开。 现在,需要检查是否有遗漏的步骤,确保示例代码正确,并且符合用户的需求。比如,是否正确使用RDelayedQueue和RBlockingQueue的组合,是否正确处理了线程和资源管理。同时,引用部分是否正确标注,引用了用户提供的参考内容。 总结下来,回答的结构应该是先介绍Redission延迟队列的基本原理,然后分步骤讲解配置、队列创建、生产者和消费者的实现,最后给出注意事项和相关问题。确保代码示例清晰,并且有详细的注释说明每一步的作用。</think>### Redission实现延迟队列教程 Redission通过`RDelayedQueue`接口实现延迟队列功能,其核心机制是将元素暂存于中间队列,Redis自动管理到期时间后转移至目标队列[^1]。 #### 一、环境配置 1. 添加Maven依赖: ```xml <dependency> <groupId>org.redisson</groupId> <artifactId>redisson</artifactId> <version>3.16.6</version> </dependency> ``` [^2] 2. 创建Redisson客户端: ```java Config config = new Config(); config.useSingleServer().setAddress("redis://127.0.0.1:6379"); RedissonClient redisson = Redisson.create(config); ``` #### 二、队列创建 ```java // 目标队列(实际消费队列) RBlockingQueue<String> destinationQueue = redisson.getBlockingQueue("destinationQueue"); // 延迟队列(绑定目标队列,设置5秒延迟) RDelayedQueue<String> delayedQueue = redisson.getDelayedQueue(destinationQueue); delayedQueue.offer("task1", 5, TimeUnit.SECONDS); // 5秒后进入目标队列 ``` #### 三、生产者实现 ```java public class Producer { public static void addDelayedTask(RDelayedQueue<String> queue, String task, long delay) { queue.offer(task, delay, TimeUnit.SECONDS); System.out.println("添加延迟任务:" + task); } } ``` #### 四、消费者实现 ```java public class Consumer implements Runnable { private RBlockingQueue<String> queue; public Consumer(RBlockingQueue<String> queue) { this.queue = queue; } @Override public void run() { while (!Thread.currentThread().isInterrupted()) { try { String task = queue.take(); // 阻塞获取元素 System.out.println("处理任务:" + task); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } } } } ``` #### 五、启动服务 ```java // 启动消费者线程 new Thread(new Consumer(destinationQueue)).start(); // 添加测试任务 Producer.addDelayedTask(delayedQueue, "支付超时订单", 30); Producer.addDelayedTask(delayedQueue, "库存释放任务", 120); ``` #### 注意事项 1. 消息持久化:Redis默认开启RDB持久化,但建议配置AOF确保可靠性 2. 异常处理:需捕获`RedisResponseTimeoutException`等网络异常 3. 资源释放:程序退出时调用`delayedQueue.destroy()`和`redisson.shutdown()`
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值