并发之 阻塞队列

本节内容总结自《Java 并发编程的艺术》

Java中的阻塞队列

什么是阻塞队列
  1. 定义:阻塞队列是支持阻塞的插入和移除方法。支持阻塞的插入是指,当队列满时,队列会阻塞插入元素的线程,直到队列不满。支持阻塞的移除方法是指,队列为空时,获取元素的线程会等待队列变为非空。阻塞队列常用于生产者和消费者的场景,生产者是向队列中添加元素的线程,消费者是从队列中取元素的线程。

  2. 阻塞队列中提供的处理方式

    方法 抛出异常 返回特殊值 一直阻塞 超时退出
    插入方法 add(e) offer(e) put(e) offer(e, time, unit)
    移除方法 remove() poll() take() poll(time, unit)
    检查方法 element() peek() 不可用 不可用
Java 中的阻塞队列
  1. ArrayBlockingQueue:是一个用数组实现的有界阻塞队列。按照插入顺序存储元素。默认情况下不保证线程公平的访问队列,为了保证公平性,可以在new时第二个参数使用true来创建一个公平性的队列。在该队列中,公平性是通过可重入锁ReentrantLock实现的。
  2. LinkedBlockingQueue:是一个用链表实现的有界阻塞队列。队列的默认和最大长度为Integer.MAX_VALUE。
  3. PriorityBlockingQueue:是一个支持优先级的无界阻塞队列。默认情况下采用升序排列。可以自定义类实现compareTo()方法来指定元素排序规则,或者初始化队列时,指定构造参数Comparator来对元素进行排序。
  4. DelayQueue:是一个支持延时获取元素的无界阻塞队列。队列使用PriorityQueue实现,队列中的元素必须实现Delayed接口,在创建元素时可以指定多久才能从队列中获取当前元素。只有在延迟期满时才能从队列中提取元素。
    • DelayQueue的使用场景
      1. 缓存系统的设计:可以用DelayQueue保存缓存元素的有效期,使用一个线程循环查询DelayQueue,一旦能从队列中过获取到元素,表示缓存有效期已经到了。
      2. 定时任务调度:使用DelayQueue保存当天将会执行的任务和执行时间,一旦从DelayQueue中获取到任务就开始执行。
  5. SynchronousQueue:是一个不存储元素的阻塞队列。每一个put操作必须等待一个take操作,否则不能继续添加元素。他支持公平访问队列(参数为true),默认情况下线程采用非公平性策略访问队列。SynchronousQueue可以看成是一个传球手,负责把生产者线程处理的数据直接传递给消费者线程。队列本身不存储任何元素,非常适合传递性场景。
  6. LinkedTransferQueue:是一个由链表结构组成的无界阻塞队列。相对于其他阻塞队列,LinkedTransferQueue队列多了transfer和tryTransfer方法。
    1. transfer方法:如果当前有消费者正在等待接收元素,transfer方法可以把生产者传入的元素立刻transfer给消费者。如果没有消费者在等待接收元素,transfer方法会将元素存放在队列的tail节点,并等待该元素被消费者消费了才返回。transfer方法会自旋等待消费者线程,但自旋一定次数之后也可以使用Thread.yield()方法来暂停自旋,并执行其他线程。
    2. tryTransfer方法:是用来试探生产者传入的元素是否能直接给消费者。如果没有消费者接收元素则返回false。和transfer方法的区别是,不论消费者是否存在,该方法都会立即返回。有时间限制的tryTransfer(E e, long timeout, TimeUnit unit)方法,试图把生产者传入的元素直接传给消费者,但是如果没有消费者消费该元素则等待指定时间再返回。
  7. LinkedBlockingDeque:是一个由链表结构组成的双向阻塞队列。它支持两端插入和删除元素。双向队列因为多了一个操作队列的入口和出口,在多线程同时入队时,也就减少了一般的竞争。
阻塞队列的实现分析
  • 可以使用等待通知机制实现,就是当生产者往满的队列中插入元素时会阻塞住生产者,消费者消费了一个队列中的元素后会通知生产者停止阻塞。可以使用 Object类中的等待通知机制,或者 Condition实现。
posted @ 2021-03-29 22:22  Cruel_King  阅读(50)  评论(0编辑  收藏  举报