mq - 优先队列PriorityQueue

场景描述

队列通常采用 FIFO(先进先出) 策略,可以满足多数情况下的开发需求,但是也有其不足之处。

例如:

我要小明帮我买早餐,
第一次,“小明,你后天帮我买牛奶吧!”,
过了一会儿,“小明,你明天帮我买豆浆呗!”。

这个案例中,我先叫小明买的牛奶,再叫他买的豆浆,如果按照普通队列,按顺序执行,先买牛奶显然是错的。

优先队列

优先队列满足了上述的开发需求,队列中的元素按照自然顺序(Comparator)进行排序,通过Comparable接口可以定制任意的优先策略。

常见的优先队列有:

  • PriorityQueue 优先队列
  • PriorityBlockingQueue 阻塞优先队列(线程安全的队列)
  • DelayQueue 延迟队列(函数使用了可重入锁,是线程安全的队列),延迟队列所有元素必须继承Delayed接口,Delayed接口又继承自Comparable接口,专门用于时间的比较。

这三者优先队列之间不存在继承关系,DelayQueue内部包含PriorityQueue实例,是一个优先队列的装饰者;PriorityBlockingQueue源码也不同与另外两个。

扩展

在高级的队列中(比如 activemq),也是有类似的功能,如果有业务需求,可以调整配置启用功能。

案例

复用上一个文章的工具类,通过PriorityBlockingQueue实现上述中买早餐的案例,顺便展示Delayed接口的使用方法。

package com.sea.common.util;

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

class DelayItem implements Delayed {
    private long milli;
    private String msg;

    @Override
    public String toString() {
        return msg;
    }

    public DelayItem(long milli, String msg) {
        this.milli = milli;
        this.msg = msg;
    }

    @Override
    public long getDelay(TimeUnit unit) {
        /**
         * TimeUnit提供了时间转换的算法,如果确定了延迟的精度,直接return milli即可
         */
        return unit.convert(milli, TimeUnit.MILLISECONDS);
    }

    @Override
    public int compareTo(Delayed o) {
        return this.milli == o.getDelay(TimeUnit.NANOSECONDS) ? 0
                : (this.milli > o.getDelay(TimeUnit.NANOSECONDS) ? 1 : -1);
    }
}

public class Test {
    public static void main(String[] args) throws InterruptedException {
        Handler<DelayItem> handler = new Handler<DelayItem>() {

            @Override
            public void handleMessage(DelayItem msg) throws Exception {
                Thread.sleep(1000);
                System.out.println(msg);
            }
        };

        BlockingLopper<DelayItem> lopper = new BlockingLopper<DelayItem>();

        lopper.setDummy(new DelayItem(Long.MAX_VALUE, ""));
        lopper.setHandler(handler);
        lopper.setQueue(new PriorityBlockingQueue<>(2));
        lopper.loop();

        lopper.offer(new DelayItem(200, "小明,你后天帮我买牛奶吧!"));
        lopper.offer(new DelayItem(100, "小明,你明天帮我买豆浆呗!"));
        System.out.println("--------------------");
        lopper.stopLoop();
    }
}

内容补充

在java-api中还有几个比较抢眼的名词,因为不是十分常用,这里简单介绍。

ConcurrentLinkedQueue同步线性队列,一个基于链接节点的无界线程安全队列。此队列按照 FIFO(先进先出)原则对元素进行排序。针对线程安全问题,java.util.concurrent 包下实现了多个线程安全的Collection和Map,名字以Concurrent开头,util包下有对应的非线程安全实现类,ConcurrentLinkedQueue与LinkedList功能相对应。

Deque 一个线性的 collection,支持在两端插入和移除元素。名称 deque 是“double ended queue(双端队列)”的缩写,通常读为“deck”。大多数 Deque 实现对于它们能够包含的元素数没有固定限制,但此接口既支持有容量限制的双端队列,也支持没有固定大小限制的双端队列。Deque看似更加灵活,然而实际开发远不及普通队列有用。

posted on   疯狂的妞妞  阅读(174)  评论(0编辑  收藏  举报

(评论功能已被禁用)
相关博文:
阅读排行:
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
< 2025年3月 >
23 24 25 26 27 28 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31 1 2 3 4 5

导航

统计

点击右上角即可分享
微信分享提示