BlockingQueue 是Java.util.concurrent包下重要的数据结构,区别于普通的队列,BlockingQueue提供了线程安全的队列访问方式,并发包下很多高级同步类的实现都是基于BlockingQueue实现的,一般用于生产者-消费者模式,生产者就是往队列里添加元素的线程,消费者是从队列里拿元素的线程,BlockingQueue就是存放元素的容器。
BlockingQueue的操作方法
阻塞队列提供了四组不同的方法用于插入、移除、检查元素:
处理方法 | 抛出异常 | 返回特殊值 | 一直阻塞 | 超时退出 |
---|---|---|---|---|
插入方法 | add(e) | offer(e) | put(e) | offer(e,time,unit) |
移除方法 | remove() | poll() | take() | poll(time,unit) |
检查方法 | element() | peek() | - | - |
BlockingQueue的实现类
ArrayBlockingQueue:由数组结构组成的有界阻塞队列,内部结构是数组,可以初始化队列大小,一旦初始化不能改变,构造方法中的fair表示控制对象的内部锁是否采用公平锁,默认是非公平锁;
LinkedBlockingQueue:由链表结构组成的有界队列,内部结构是链表,默认队列大小为
Integer.MAX_VALUE
,也可以指定大小,按照先进先出的原则对元素进行排序;
DelayQueue:该队列中的元素只有当其指定的延迟时间到了,才能从队列中获取到该元素,注入其中的元素必须事先DelayQueue是一个没有大小限制的队列,因此往队列中插入数据的操作,永远不会被阻塞。
PriorityBlockingQueue:基于优先级的无界阻塞队列,内部控制线程同步的优先级;
不会阻塞数据生产者因为是无界队列,而只会在没有可消费数据时,阻塞数据的消费者,因此使用时要注意,生产者生产数据绝对不能快鱼消费者消费的数据,否则时间一长,最终会好近所有的可用堆内存空间。
SynchronousQueue:这个队列,没有任何内部容量,并且每一个put必须等待一个take。
阻塞队列的原理
阻塞队列利用了Lock锁的多条件阻塞控制;
put流程
-
所有执行put操作的线程竞争lock锁,拿到lock锁的线程进入下一步,没有拿到lock锁的线程自选竞争锁;
-
判断阻塞队列是否满了,如果满了,则调用await方法阻塞这个线程,并标记为notFull(生产者)线程,同时释放lock锁,等待被消费者线程唤醒;
-
如果没有满,则调用enqueue方法将元素put进阻塞队列,注意这一步的线程还有一种情况是第二步中阻塞的线程被唤醒且又拿到了lock锁的线程。
-
唤醒一个标记为notEmpty(消费者)的线程。
take流程
-
所有执⾏take操作的线程竞争lock锁,拿到了lock锁的线程进⼊下⼀步,没有 拿到lock锁的线程⾃旋竞争锁。
-
判断阻塞队列是否为空,如果是空,则调⽤await⽅法阻塞这个线程,并标记 为notEmpty(消费者)线程,同时释放lock锁,等待被⽣产者线程唤醒。
-
如果没有空,则调⽤dequeue⽅法。注意这⼀步的线程还有⼀种情况是第⼆步 中阻塞的线程被唤醒且⼜拿到了lock锁的线程。
-
唤醒⼀个标记为notFull(⽣产者)的线程。