延时队列小例子

本文详细介绍如何使用Java的DelayQueue实现顾客到达设定时间自动下机的功能,通过自定义Person类实现Delayed接口,并创建NetBar类来管理顾客的上机与下机过程。

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

用Java的延时队列,实现顾客到了点就自动下机。

所谓延时队列,就是不到固定要求,不能从里面取值,即便取值,取值也为null。 

对比其他队列:

SynchronousQueue 队列不能存放数据,存放数据就报错,但是可以先开一个线程持续从其中取值,另一个线程放数据
LinkedBlockingQueue 阻塞有界队列,如果不指定大小,可存放多个值
ArrayBlockingQueue 阻塞无界队列,必须指定大小,超过添加就报错
ConcurrentLinkedQueue 高性能无阻塞无界队列
PriorityBlockingQueue 优先级队列,存放值时不排序,取值时开始排序

DelayQueue 延时无界队列,只有到了设定的条件下才能取值

 

package cn.base;

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

/**
 * @Description : 需要放入延时队列的对象,需要实现Delayed接口
 * @Author : 小书包
 * @CreateDate :2018-11-23 18:03
 */
public class Person implements Delayed{
    // 用户名
    private String name;
    // 身份证号
    private int id;
    // 上机金额
    private int money;
    // 结束时间
    private long endTime;
    // 配置当前计算的时间单位,毫秒
    private TimeUnit timeUnit = TimeUnit.MILLISECONDS;

    public Person(String name, int id, int money) {
        this.name = name;
        this.id = id;
        this.endTime = money * 1000 + System.currentTimeMillis();
    }

    /**
     * 结束时间与当前时间进行比较,返回结果
     * @param unit
     * @return
     */
    @Override
    public long getDelay(TimeUnit unit) {
        return endTime - System.currentTimeMillis();
    }

    /**
     * 判断是否可以取出
     * @param o
     * @return
     */
    @Override
    public int compareTo(Delayed o) {
        Person person = (Person) o;
        return (int) (this.getDelay(this.timeUnit) - person.getDelay(this.timeUnit));
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public int getMoney() {
        return money;
    }

    public void setMoney(int money) {
        this.money = money;
    }
}
package cn.base;

import java.util.concurrent.DelayQueue;

/**
 * @Description : 需要实现Runnable接口
 * @Author : 小书包
 * @CreateDate :2018-11-23 18:09
 */
public class NetBar implements Runnable {
    // 是否正常营业
    private volatile boolean isRun = true;
    // 延时队列
    private DelayQueue<Person> queue = new DelayQueue<>();

    /**
     * 顾客上机
     * @param person
     */
    void surfNet(Person person){
        System.out.println("顾客:"+person.getName()+" 身份证号:"+person.getId()+"交钱:"+person.getMoney()+"块,开始上机...");
        queue.offer(person);
    }

    /**
     * 顾客下机
     * @param person
     */
    void offlineNet(Person person){
        System.out.println("顾客:"+person.getName()+" 身份证号:"+person.getId()+"时间已到,开始下机...");
    }


    @Override
    public void run() {
        while (isRun) {
            try {
                // 持续获取,如果获取不到则将继续获取
                Person person = queue.take();
                // 获得人员之后,开始让他下线
                offlineNet(person);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

        }
    }

    public static void main(String[] args) {
        NetBar netBar = new NetBar();
        System.out.println("网吧开始营业");
        new Thread(netBar).start();

        netBar.surfNet(new Person("张三",2,4));
        netBar.surfNet(new Person("李四",2,4));
        netBar.surfNet(new Person("王五",2,4));
        netBar.surfNet(new Person("路六",2,4));
    }
}
网吧开始营业
顾客:张三 身份证号:2交钱:0块,开始上机...
顾客:李四 身份证号:2交钱:0块,开始上机...
顾客:王五 身份证号:2交钱:0块,开始上机...
顾客:路六 身份证号:2交钱:0块,开始上机...
=============
顾客:张三 身份证号:2时间已到,开始下机...
顾客:路六 身份证号:2时间已到,开始下机...
顾客:王五 身份证号:2时间已到,开始下机...
顾客:李四 身份证号:2时间已到,开始下机...

 

<think>嗯,用户想了解死信队列和延时队列,我需要先理清楚这两个概念的区别和联系。先从基础开始,死信队列应该和消息队列中的消息处理失败有关,而延时队列可能用于定时任务或者延迟处理。 首先,死信队列(DLQ)是什么?我记得当消息无法被正常消费时,会被转移到DLQ,这样可以避免消息丢失,同时方便后续排查问题。需要解释一下消息变成死信的条件,比如重试次数超限、消息过期、被拒绝等。然后,应用场景可能包括异常处理、监控报警、重试机制等。比如电商订单超时关闭,可能需要用死信队列来处理未支付的订单。 然后是延时队列,用户提到的是否是消息队列中的延迟消息功能。比如RabbitMQ可以用死信队列加TTL来实现,而RocketMQ有内置的延迟级别。延时队列的常见用途比如订单超时、定时通知、重试策略。需要说明实现方式,不同消息中间件的支持情况。 接下来要考虑两者的关联。比如延时队列的实现可能依赖死信队列,当消息过期后变成死信,然后被转发到死信队列,消费者再从死信队列获取处理。这种情况下,死信队列和延时队列是结合使用的。 需要检查技术细节是否正确,比如RabbitMQ的插件和RocketMQ的延迟功能。另外,是否需要举例说明,比如订单超时的流程,用户下单后发送延迟消息,30分钟后检查支付状态,未支付则关单。这时使用延时队列,可能通过设置消息TTL,过期后进入死信队列,消费者处理关单逻辑。 还要注意用户可能的疑问点,比如死信队列和延时队列的区别,是否所有消息系统都支持,如何选择实现方式。可能用户在实际应用中遇到问题,需要了解如何配置和使用这些队列。 最后,结构要清晰,分点讲解,先定义,再应用场景,实现方式,结合例子。确保语言准确,避免技术错误,比如RabbitMQ的延迟消息需要插件,而RocketMQ原生支持。同时使用中文,符合用户要求。</think>好的,我将分步为您解释死信队列(Dead Letter Queue)和延时队列(Delay Queue)的概念、作用及实现原理: 1. **死信队列(DLQ)** - **定义** 当消息队列中的消息无法被正常消费时,会被转移到专属的"死信队列"中存储。这种机制用于保证异常消息不会丢失且可追踪。 - **消息成为死信的条件** - 消息被消费者明确拒绝(basic.reject或basic.nack) - 消息超过存活时间(TTL)未被消费 - 队列达到最大长度限制时被丢弃的消息 - 消息重试次数超过预设阈值 - **核心作用** $$ \text{异常处理} + \text{数据兜底} + \text{系统解耦} $$ 通过监控死信队列,开发者可以: ▶️ 排查消费失败原因 ▶️ 实现自动重试机制 ▶️ 记录错误日志 ▶️ 触发报警通知 2. **延时队列** - **定义** 允许消息在指定延迟时间后才被消费者获取的特殊队列,常用于需要定时触发的业务场景。 - **实现方式** | 实现方案 | 原理示例 | 适用场景 | |-------------------------|------------------------------|-----------------------| | TTL+死信队列(RabbitMQ)| 设置消息TTL,过期后转入DLQ | 简单延迟任务 | | 轮询方案(Redis ZSET) | 用有序集合存储执行时间戳 | 高精度延迟任务 | | 时间轮算法(Kafka) | 分层时间轮管理延迟消息 | 海量延迟消息处理 | 3. **典型应用场景对比** - **死信队列用例** ```java // 伪代码示例:电商订单超时关单 orderService.sendMessage(orderId, TTL=30min); // 发送30分钟有效期的订单消息 // 若30分钟后未被正常消费(未完成支付),消息进入DLQ dlqConsumer.processDeadMessage(orderId -> closeOrder(orderId)); ``` - **延时队列用例** ```python # 伪代码示例:外卖平台骑手推送 def send_reminder(order_id): delay_queue.push(msg=order_id, delay=15*60) # 15分钟延迟 consumer.on_message(lambda: send_push("订单即将超时!")) ``` 4. **技术选型建议** - RabbitMQ:通过`x-dead-letter-exchange`插件实现 - RocketMQ:原生支持18个延迟级别(1s/5s/10s/30s/1m...2h) - Kafka:需自行实现时间轮或借助外部存储 - Redis:使用`zadd`+`zrangebyscore`实现精准延迟 5. **注意事项** - 避免消息循环:死信队列的DLX不要指向原始队列 - 时钟同步问题:分布式环境下确保服务器时间一致 - 监控指标:需跟踪`dead_letter_count`和`delay_message_accumulation` 两者的核心差异在于: $$ \text{死信队列} \rightarrow \text{被动处理异常} \quad vs \quad \text{延时队列} \rightarrow \text{主动控制时序} $$ 实际开发中常组合使用,例如通过设置消息TTL实现延迟效果,超时后消息进入死信队列触发后续处理。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值