阻塞队列(BlockingQueue)
概念
-
首先它是一个队列,而一个阻塞队列在数据结构中所起的作用大致如图所示:
-
当阻塞队列是空时,从队列中获取元素的操作将会被阻塞
-
当阻塞队列是满时,往队列里添加元素的操作将会被阻塞
核心方法
-
抛异常:如果操作不能马上进行,则抛出异常
-
特定的值:如果操作不能马上进行,将会返回一个特殊的值,一般是 true 或者 false
-
阻塞:如果操作不能马上进行,操作会被阻塞
-
超时:如果操作不能马上进行,操作会被阻塞指定的时间,如果指定时间没执行,则返回一个特殊值,一般是 true 或者 false
实现类
官方的7种实现:
- ArrayBlockingQueue :一个由数组结构组成的有界阻塞队列,此队列按 FIFO(先进先出)对元素进行排序
- LinkedBlockingQueue :一个由链表结构组成的有界阻塞队列,此队列按 FIFO(先进先出)对元素进行排序,吞吐量通常要高于ArrayBlockingQueue。此队列的默认和最大长度为 Integer.MAX_VALUE,可以认为是无界
- PriorityBlockingQueue :一个支持优先级排序的无界阻塞队列
- DelayQueue:一个使用优先级队列实现的无界阻塞队列
- SynchronousQueue:一个不存储元素的阻塞队列,每个插入操作必须等到另一个线程调用移除操作,否则插入操作一直处于阻塞状态,吞吐量通常要高于LinkedBlokcingQueue
- LinkedTransferQueue:一个由链表结构组成的无界阻塞队列
- LinkedBlockingDeque:一个由链表结构组成的双向阻塞队列
ArrayBlockingQueue 和LinkedBlockingQueue区别
- 底层实现不同
- ArrayBlockingQueue 底层使用数组来维护队列
- LinkedBlockingQueue 底层使用链表来维护队列,在添加和删除队列中的元素的时候,会创建和销毁节点对象,在高并发和大量数据的时候,GC压力很大
- 锁的方式不同
- ArrayBlockingQueue 获取数据和添加数据都是使用同一个锁对象,不过,在ArrayBlockingQueue 中使用Condition的等待/通知机制,这样使得ArrayBlockingQueue的数据写入和获取操作已经足够轻巧,以至于引入独立的锁机制,除了给代码带来额外的复杂性外,其在性能上完全占不到任何便宜
- LinkedBlockingQueue 获取数据和添加数据使用不同的锁对象
SynchronousQueue
实际上它不是一个真正的队列,因为它不会为队列中元素维护存储空间。与其他队列不同的是,它维护一组线程,这些线程在等待着把元素加入或移出队列
public class SynchronousQueueDemo { public static void main(String[] args) { SynchronousQueue<Integer> synchronousQueue = new SynchronousQueue<>(); new Thread(() -> { try { synchronousQueue.put(1); TimeUnit.SECONDS.sleep(3); synchronousQueue.put(2); TimeUnit.SECONDS.sleep(3); synchronousQueue.put(3); } catch (InterruptedException e) { e.printStackTrace(); } }).start(); new Thread(() -> { try { Integer val = synchronousQueue.take(); System.out.println(LocalDateTime.now().toString() + "---------" + val); Integer val2 = synchronousQueue.take(); System.out.println(LocalDateTime.now().toString() + "---------" + val2); Integer val3 = synchronousQueue.take(); System.out.println(LocalDateTime.now().toString() + "---------" + val3); } catch (InterruptedException e) { e.printStackTrace(); } }).start(); } } // 输出: // 2020-06-18T10:21:07.615---------1 // 2020-06-18T10:21:10.496---------2 // 2020-06-18T10:21:13.497---------3
使用场景
- 生产者消费者模式
- 线程池
- 消息中间件