之前有介绍几种延迟队列的对方,如java并发编程学习之DelayQueueActiveMQ - 延迟队列RabbitMQ - 延迟队列,对于延迟队列,我还是推荐用以上几种,这边只对redis如何实现延迟队列做了一个例子。
为了实现延迟队列,我们需要定义两个类型的数据:

  1. 队列,需要执行的任务,直接放入队列,线程通过队列的内容进行执行任务。
  2. 有序集合,通过成员的分数用来判断是否可以放入队列执行。我们往有序集合插入数据的时候,分数就是当前时间+延迟的时间,判断的时候,就可以通过当前时间和分数进行比较,如果当前时间大于分数,说明还没到执行的时候。如果小于等于分数,则放入队列执行。

示例

把任务加入到有序集合,如果分数为0,说明没有延迟,直接加入到队列中。如果分数不为0,说明有延迟,把当前时间加上延迟时间,作为分数存入有序集合中。

private static void add(double score) {
    SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    String format = formatter.format(new Date());
    // 马上执行的任务
    if (score == 0) {
        JedisUtils.rpush(queue, format);
    } else {
        double date = System.currentTimeMillis() + score;
        JedisUtils.zadd(delay, date, format + "-" + score);
    }
}

队列处理任务。如果取到任务,直接执行,如果没有任务,则休眠10毫秒。

static String queue = "queue:";
static String delay = "delay:";

static class QueueThread implements Runnable {
    @Override
    public void run() {
        while (true) {
            String res = JedisUtils.lpop(queue);
            if (null != res) {
                SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                System.out.println(formatter.format(new Date()) + "---" + "处理消息:" + res);
            } else {
                //暂时没有消息,就休眠10毫秒
                try {
                    TimeUnit.MILLISECONDS.sleep(10);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

延迟任务处理。在while循环中 ,先根据小于当前时间的分数取有序集合的数据,如果有数据,说明存在马上执行的任务,把任务从有序集合移除,并加入到队列中。

static class DelayThread implements Runnable {

    @Override
    public void run() {
        while (true) {
            long now = System.currentTimeMillis();
            Set<String> set = JedisUtils.zrangeByScore(delay, Double.MIN_VALUE, now);
            // 如果有可以运行的,则从有序集合移除,并放入队列
            if (set.size() > 0) {
                Iterator<String> iterator = set.iterator();
                while (iterator.hasNext()) {
                    String next = iterator.next();
                    JedisUtils.zrem(delay, next);
                    JedisUtils.rpush(queue, next);
                }
            } else {
                // 没有内容,则休眠10毫秒
                try {
                    TimeUnit.MILLISECONDS.sleep(10);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

测试例子,

@Test
public void testDelayQueue() throws InterruptedException {
    new Thread(new QueueThread()).start();
    new Thread(new DelayThread()).start();
    add(2000);
    add(3000);
    add(0);
    add(4000);
    add(5000);
    add(6000);
    TimeUnit.SECONDS.sleep(10);
}

运行结果如下:

2020-09-24 22:36:59---处理消息:2020-09-24 22:36:59
2020-09-24 22:37:01---处理消息:2020-09-24 22:36:59-2000.0
2020-09-24 22:37:02---处理消息:2020-09-24 22:36:59-3000.0
2020-09-24 22:37:03---处理消息:2020-09-24 22:36:59-4000.0
2020-09-24 22:37:04---处理消息:2020-09-24 22:36:59-5000.0
2020-09-24 22:37:05---处理消息:2020-09-24 22:36:59-6000.0

大军
847 声望183 粉丝

学而不思则罔,思而不学则殆


« 上一篇
redis - 信号量
下一篇 »
redis - 持久化