简介

阻塞队列支持两个特性:1.当队列为空时,阻塞获取队列中元素的线程;2.当队列已满时,阻塞插入队列中元素的线程。J.U.C 中提供的高效且线程安全的队列,可以帮助我们更快速地编写多线程程序。

java 同步阻塞队列 java中阻塞队列_阻塞队列


核心元素

  • ArrayBlockingQueue:队列为定长数组,生产消费共用一把锁(默认非公平);
  • LinkedBlockingQueue:队列为链表,采用独立锁;
  • DelayQueue:无大小限制,插入不会阻塞,
  • PriorityBlockingQueue:非公平锁
  • SynchronousQueue:无缓冲的阻塞队列

队列的原理

ArrayBlockingQueue

  • 基于数组实现,队列中的元素遵循 FIFO,一旦创建不可改变;
  • 数组头存放在队列中停留时间最长的元素,获取元素会从头部获取;反之亦然;
  • 生产消费公用一把锁(默认非公平),两个条件,notEmpty,notFull;

LinkedBlockingQueue

  • 基于链表实现,队列中的元素遵循FIFO,可选容量(默认Integer.MAX_VALUE);
  • 两把锁,两个条件;takeLock-notEmpty;putLock-notFull;

PriorityBlockingQueue

  • 组合 PriorityQueue(仅用于序列化),不接受null元素和不能够比较的元素;
    如果不使用自定义的比较器,则队列中的元素必须实现Comparable接口;
  • 底层物理结构采用数组数据结构,逻辑结构采用平衡二叉堆(小根堆,堆排序),初始化的容量大小为11,最大上限接受 Integer.MAX_VALUE-8,超大容量时可能会造成 OutOfMemoryError;
  • 当队列中的元素处于同一优先级时,此队列中的元素操作并不能保证这些元素的访问顺序;如果需要一个严格的顺序,可以自己定义一个第二排序关键字(demo参见jdk文档);
  • 一把锁(非公平,不可选),一个条件,lock-not Empty;

DelayQueue<E extends Delayed>

  • 队列中的元素必须实现 Delayed 接口,只有到期元素(到期仅在元素的 getDelay(TimeUnit.NANOSECONDS) 方法返回值小于等于零时发生)才能取出;但是队列的size方法会计算到期和未到期的元素总数;此队列中不允许插入null元素;
  • 组合 PriorityQueue,所有队列操作基于 PriorityQueue;
  • leader,主从模式,用于优化阻塞通知的线程元素leader
  • 一把锁(非公平,不可选),一个条件,lock-available;

使用技巧

  • 阻塞队列不接受null的元素,add, put or offer 一个null值会抛出NullPointerException;
  • 阻塞队列可能有容量限制,阻塞队列没有容量限制的默认容量为 Integer.MAX_VALUE(231-1)
  • 阻塞队列主要被设计用于生产者和消费者的队列,但却实现了集合的接口;因此一些操作并不是很高效,比如 remove 方法,但却故意保留下来,用于个边场景,比如队列中的消息被取消了;
  • 阻塞队列是线程安全的,所有的队列操作都是通过内部锁(ReentrantLock)或其他形式的并发手段实现的原子操作。但是批量的集合操作,并不一定是原子操作,例如 addAll, containsAll , retainAll and removeAll;
  • 阻塞队列并不支持 close 或 shutdown 的操作来表明没有元素将会被添加,这种需求应该独立实现,比如通用的策略是生产者插入一个特殊对象,消费者获取到这个对象后,理解为队列中不会再有元素。
  • 内存一致性原则:插入元素a的操作先行发生( happen-before)于访问或移除元素a的操作

应用场景

  • ArrayBlockingQueue, LinkedBlockingQueue - 生产消费者模式问题,队列元素FIFO;
  • PriorityBlockingQueue - 自定义优先级排列
  • DelayQueue - 缓存系统设计,任务超时处理,替代定时任务扫描的业务场景