六种延迟队列的实现方式,你知道几种?

开发 前端
DelayQueue​ 是 Java 并发包 java.util.concurrent​ 下的一个线程安全的阻塞队列,它存储的元素必须实现 Delayed 接口,以便计算元素的延时时间。队列中的元素只有在其指定的延迟时间到达之后才能从队列中取出。

1. DelayQueue 延时队列

DelayQueue 是 Java 并发包 java.util.concurrent 下的一个线程安全的阻塞队列,它存储的元素必须实现 Delayed 接口,以便计算元素的延时时间。队列中的元素只有在其指定的延迟时间到达之后才能从队列中取出。

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

public class Order implements Delayed {
    private long time;
    private String name;

    public Order(String name, long delay, TimeUnit unit) {
        this.name = name;
        this.time = System.currentTimeMillis() + unit.toMillis(delay);
    }

    @Override
    public long getDelay(TimeUnit unit) {
        long delay = time - System.currentTimeMillis();
        return unit.convert(delay, TimeUnit.MILLISECONDS);
    }

    @Override
    public int compareTo(Delayed other) {
        if (this.time < ((Order) other).time) {
            return -1;
        } else if (this.time > ((Order) other).time) {
            return 1;
        }
        return 0;
    }
}

public class DelayQueueDemo {
    public static void main(String[] args) throws InterruptedException {
        DelayQueue<Order> delayQueue = new DelayQueue<>();
        delayQueue.put(new Order("Order1", 5, TimeUnit.SECONDS));
        delayQueue.put(new Order("Order2", 10, TimeUnit.SECONDS));
        delayQueue.put(new Order("Order3", 15, TimeUnit.SECONDS));

        System.out.println("订单延迟队列开始时间:" + java.time.LocalDateTime.now());
        while (delayQueue.size() != 0) {
            Order order = delayQueue.take(); // 阻塞直到元素可用
            System.out.format("订单: %s 被取消, 取消时间: %s\n", order.name, java.time.LocalDateTime.now());
        }
    }
}

2. Quartz 定时任务

Quartz 是一个开源的任务调度库,可以集成到几乎任何Java应用中,用于定时执行任务。通过定义任务和触发器,可以很容易地实现定时任务。

import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.springframework.scheduling.quartz.QuartzJobBean;

public class QuartzJob extends QuartzJobBean {
    @Override
    protected void executeInternal(JobExecutionContext context) throws JobExecutionException {
        System.out.println("执行定时任务: " + System.currentTimeMillis());
    }
}

// 在 Spring 配置文件中配置 Quartz
// ...

3. Redis sorted set

Redis 的有序集合(sorted set)可以利用 score 来实现延时队列。通过设置元素的 score 为过期时间戳,可以实现在特定时间自动过期并被消费。

import redis.clients.jedis.Jedis;

public class RedisDelayQueue {
    private static final String DELAY_QUEUE = "delayQueue";

    public void addToQueue(String key, long delaySeconds) {
        double score = System.currentTimeMillis() / 1000 + delaySeconds;
        new Jedis().zadd(DELAY_QUEUE, score, key);
    }

    public void consume() {
        long now = System.currentTimeMillis() / 1000;
        while (true) {
            Set<String> keys = new Jedis().zrangeByScore(DELAY_QUEUE, 0, now);
            for (String key : keys) {
                new Jedis().zrem(DELAY_QUEUE, key);
                System.out.println("消费元素: " + key);
            }
            if (keys.isEmpty()) {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

4. Redis 过期回调

Redis 可以配置过期事件通知,当一个键过期时,Redis 会发送一个事件通知给订阅了该事件的客户端。

import org.springframework.data.redis.connection.Message;
import org.springframework.data.redis.connection.MessageListener;
import org.springframework.data.redis.listener.ChannelTopic;
import org.springframework.data.redis.listener.RedisMessageListenerContainer;
import org.springframework.data.redis.listener.adapter.MessageListenerAdapter;

public class RedisKeyExpirationListener implements MessageListener {
    @Override
    public void onMessage(Message message, byte[] pattern) {
        String expiredKey = new String(message.getBody());
        System.out.println("监听到key:" + expiredKey + "已过期");
    }
}

// 在 Spring 配置中配置 RedisMessageListenerContainer
// ...

5. RabbitMQ 延时队列

RabbitMQ 通过消息的 TTL(Time To Live)和死信交换机(DLX)来实现延时队列。

import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.amqp.core.Message;

public class RabbitMQDelayQueue {
    private final RabbitTemplate rabbitTemplate;

    public RabbitMQDelayQueue(RabbitTemplate rabbitTemplate) {
        this.rabbitTemplate = rabbitTemplate;
    }

    public void sendDelayedMessage(String message, long delay) {
        Message msg = new Message(message.getBytes(), new MessageProperties() {
            {
                setExpiration(String.valueOf(delay));
                // 设置消息的其他属性
            }
        });
        rabbitTemplate.send("delayQueueExchange", "delayQueueRoutingKey", msg);
    }

    // 配置交换机、队列和绑定
    // ...
}

6. 时间轮算法

时间轮算法是一种高效的定时任务管理算法,Netty 提供了 HashedWheelTimer 来实现时间轮。

import io.netty.util.HashedWheelTimer;
import io.netty.util.Timer;
import io.netty.util.TimerTask;

public class NettyDelayQueue {
    public static void main(String[] args) {
        Timer timer = new HashedWheelTimer();
        timer.newTimeout(new TimerTask() {
            @Override
            public void run(Timeout timeout) throws Exception {
                System.out.println("任务执行: " + System.currentTimeMillis());
            }
        }, 5, TimeUnit.SECONDS);
    }
}
责任编辑:武晓燕 来源: 程序猿技术充电站
相关推荐

2023-10-30 11:53:37

继承JS父类

2022-03-28 20:57:31

私有属性class属性和方法

2019-09-02 11:14:08

隔离虚拟机操作系统

2021-12-15 23:10:34

JS Debugger 前端开发

2021-12-08 10:47:35

RabbitMQ 实现延迟

2018-08-03 16:40:06

前端前端框架微服务

2023-05-10 13:58:13

服务限流系统

2017-06-26 10:35:58

前端JavaScript继承方式

2021-08-05 07:28:25

Java实现方式

2009-02-11 09:46:00

ASON网络演进

2024-05-10 07:44:23

C#进程程序

2018-07-04 11:02:23

无线传输模式

2021-05-07 16:19:36

异步编程Java线程

2019-05-16 13:00:18

异步编程JavaScript回调函数

2022-03-23 12:55:50

农业物联网

2017-07-20 08:47:19

网页加载时间移动开发IT

2020-07-31 11:12:39

安全威胁网络攻击网络安全

2020-04-27 09:00:00

双因素认证身份认证生物识别

2022-01-14 10:34:50

黑客隐藏踪迹网络安全

2020-10-16 15:06:59

开发技术方案
点赞
收藏

51CTO技术栈公众号