【当年笔记】集合之Queue队列

1)Queue 分类

  • 双端队列:双端队列(Deque)是 Queue 的子类也是 Queue 的补充类,头部和尾部都支持元素插入和获取。
  • 阻塞队列:阻塞队列指的是指添加或删除元素时,如果没有成功,会阻塞并挂起等待,直到操作执行完成。
  • 非阻塞队列:非阻塞队列指的是指添加或删除元素时,如果没有成功,会直接返回操作的结果。双端队列也属于非阻塞队列。

阻塞队列

1)BlockingQueue

常用方法

插入元素:

  • add(E):添加元素到队尾,成功返回 true,失败抛出异常;
  • offer(E):添加元素到队尾,成功返回 true,失败返回 false ;
  • offer(E o,Long timeOut,Timme):添加元素到队尾,成功返回 true,失败返回 false ;
  • put(E):添加元素到队尾,如果该队列已满,则一直阻塞。
    移除元素:
  • remove(Object):移除指定元素,成功返回 true,失败返回 false;
  • poll(): 获取并移除队列的首个元素,如果队列为空,则返回 null;
  • take():获取并移除队列首个元素,如果队列尾空,则一直阻塞。
    查看元素:
  • peek():获取但不移除队列的首个元素,若队列为空,则返回 null。

2)ArrayBlockingQueue

是一个有边界的阻塞队列,它的内部实现是一个数组。它的容量是有限的,我们必须在其初始化的时候指定它的容量大小,容量大小一旦指定就不可改变。
ArrayBlockingQueue 不允许元素为 null
这个类新增offer(E e, long timeout, TimeUnit unit)方法:添加失败后会等待一段时间,超时后返回false。

3)LinkedBlockingQueue

是一个由链表实现的有界阻塞队列,容量默认值为Integer.MAX_VALUE,也可以自定义容量,建议指定容量大小,默认大小在添加速度大于删除速度情况下有造成内存溢出的风险

4)DelayQueue

DelayQueue 是一个支持延时获取元素的无界阻塞队列,队列中的元素必须实现 Delayed接口,在创建元素时可以指定延迟时间,只有到达了延迟的时间之后,才能获取到该元素。

非阻塞队列

ConcurrentLinkedQueue是一个基于链接节点的无界线程安全队列。

优先级队列

PriorityQueue 一个基于优先级堆的无界优先级队列。可根据构造队列时提供的 Comparator排序,该队列不许有null。

  • PriorityQueue 是非线程安全的,在多线程情况下可使用 PriorityBlockingQueue 类替代

1.ArrayBlockingQueue 和 LinkedBlockingQueue 的区别是什么?

ArrayBlockingQueue 和 LinkedBlockingQueue 都实现自阻塞队列
BlockingQueue,它们的区别主要体现在以下几个方面:

  • ArrayBlockingQueue 使用时必须指定容量值,LinkedBlockingQueue 可以不用指定;
  • ArrayBlockingQueue 的最大容量值是使用时指定的,并且指定之后就不允许修改;而 LinkedBlockingQueue 最大的容量为 Integer.MAX_VALUE;
  • ArrayBlockingQueue 数据存储容器是采用数组存储的;而 LinkedBlockingQueue 采用的是 Node 节点存储的。

2.Java 中常见的阻塞队列有哪些?

答:Java 中常见的阻塞队列如下:

  • ArrayBlockingQueue,由数组结构组成的有界阻塞队列;
  • PriorityBlockingQueue,支持优先级排序的无界阻塞队列;
  • SynchronousQueue,是一个不存储元素的阻塞队列,会直接将任务交给消费者,必须等队列中的添加元素被消费后才能继续添加新的元素;
  • LinkedBlockingQueue,由链表结构组成的阻塞队列;
  • DelayQueue,支持延时获取元素的无界阻塞队列

3.如何手动实现一个延迟消息队列?

可以用DelayQueue延迟队列来解决这个问题。实现思路,消息队列分为生产者和消费者,生产者用于增加消息,消费者用于获取并消费消息,我们只需要生产者把消息放入到DelayQueue 队列并设置延迟时间,消费者循环使用 take() 阻塞获取消息即可。完整的实现代码如下:

   public class CustomDelayQueue {
       // 消息编号
       static AtomicInteger MESSAGENO = new AtomicInteger(1);
   
       public static void main(String[] args) throws InterruptedException {
           DelayQueue<DelayedElement> delayQueue = new DelayQueue<>();
           // 生产者1
           producer(delayQueue, "生产者1");
           // 生产者2
           producer(delayQueue, "生产者2");
           // 消费者
           consumer(delayQueue);
       }
   
       //生产者
       private static void producer(DelayQueue<DelayedElement> delayQueue, String name) {
           new Thread(new Runnable() {
               @Override
               public void run() {
                   while (true) {
                       // 产生 1~5 秒的随机数
                       long time = 1000L * (new Random().nextInt(5) + 1);
                       try {
                           Thread.sleep(time);
                       } catch (InterruptedException e) {
                           e.printStackTrace();
                       }
                       // 组合消息体
                       String message = String.format("%s,消息编号:%s 发送时间:%s 延迟:%s 秒",
                               name, MESSAGENO.getAndIncrement(), DateFormat.getDateTimeInstance().format(new Date()), time / 1000);
                       // 生产消息
                       delayQueue.put(new DelayedElement(message, time));
                   }
               }
           }).start();
       }
   
       //消费者
       private static void consumer(DelayQueue<DelayedElement> delayQueue) {
           new Thread(new Runnable() {
               @Override
               public void run() {
                   while (true) {
                       DelayedElement element = null;
                       try {
                           // 消费消息
                           element = delayQueue.take();
                           System.out.println(element);
                       } catch (InterruptedException e) {
                           e.printStackTrace();
                       }
                   }
               }
           }).start();
       }
   
       // 延迟队列对象
       static class DelayedElement implements Delayed {
           // 过期时间(单位:毫秒)
           long time = System.currentTimeMillis();
           // 消息体
           String message;
           // 参数:delayTime 延迟时间(单位毫秒)
           public DelayedElement(String message, long delayTime) {
               this.time += delayTime;
               this.message = message;
           }
           @Override
           // 获取过期时间
           public long getDelay(TimeUnit unit) {
               return unit.convert(time - System.currentTimeMillis(), TimeUnit.MILLISECONDS);
           }
           @Override
           // 队列元素排序
           public int compareTo(Delayed o) {
               if (this.getDelay(TimeUnit.MILLISECONDS) > o.getDelay(TimeUnit.MILLISECONDS))
                   return 1;
               else if (this.getDelay(TimeUnit.MILLISECONDS) < o.getDelay(TimeUnit.MILLISECONDS))
                   return -1;
               else
                   return 0;
           }
           @Override
           public String toString() {
               // 打印消息
               return message + " |执行时间:" + DateFormat.getDateTimeInstance().format(new Date());
           }
       }
   }
   
   以上程序支持多生产者,执行的结果如下:

> 生产者1,消息编号:1 发送时间:2019-6-12 20:38:37 延迟:2 秒 |执行时间:2019-6-12 20:38:39

>

> 生产者2,消息编号:2 发送时间:2019-6-12 20:38:37 延迟:2 秒 |执行时间:2019-6-12 20:38:39

>

> 生产者1,消息编号:3 发送时间:2019-6-12 20:38:41 延迟:4 秒 |执行时间:2019-6-12 20:38:45

>

> 生产者1,消息编号:5 发送时间:2019-6-12 20:38:43 延迟:2 秒 |执行时间:2019-6-12 20:38:45


posted @ 2022-06-10 18:39  正文儿  阅读(51)  评论(0编辑  收藏  举报