前言:阻塞队列是高并发场景中使用较多的接口,本身提供了很多功能并且很利于生产者-消费者的实现。
1. 源码解析
1.1 类接口
BlockingQueue本身类结构如下:
本质上可当做队列,主要实现了queue
接口,因此具有队列的先进先出(FIFO)功能,因此从方法而言,具有Queu
及Collection
接口方法。
1. 2 接口概况解析
本质上是一个队列,但是与普通队列有不同之处,阻塞队列,只有队列非空(有元素)时才可以获取元素,当队列非满的时候才可以添加元素,否则只能阻塞等待。
BlockingQueue常见的操作可以划分为以下四种:
- 操作失败时抛出异常
- 操作失败时抛出特定的值(null或者false)
- 操作失败时会阻塞当前线程直到操作成功
- 操作失败时会阻塞一定的时间,然后放弃
功能/出错处理 | 抛异常 | 返回特定值 | 阻塞 | 超时放弃 |
---|---|---|---|---|
插入 | add(e) | offer(e) | put(e) | offer(e, time, unit) |
移除 | remove() | poll() | take() | poll(time, unit) |
插入 | element() | peek() | \ | \ |
BlockingQueue
不接收空值,当该接口实现类,试图添加(add()、 put()、 offer()
)一个null
值时,会抛出NullPointerException
,BlockingQueue可容纳的数量有限,无论何时,阻塞队列都有一个保留剩余容量(remainingCapacity
),当真正剩余容量大于该值时,可以任意的添加元素;当一个阻塞队列没有设置保留剩余容量时,会默认用Integer.MAX_VALUE
当做最大剩余容量。
支持一些集合的方法,比如可以利用Collection
移除元素方法移除元素,(remove(x))
,但是该方法使用效率较低,只是在取消消息队列时才偶尔使用。
实现类都是线程安全的。所有队列方法原子性实现利用的是内部锁或者其他形式并发控制器。但是集合多元素的操作比如addAll, containsAll, retainAll 及 removeAll
等方法,都不要求原子性。阻塞队列(BlockingQueue
)不支持类似于线程池的shutdown
方法(shutdown方法主要目的是不允许其他元素的添加)。当然各自的实现类可以根据自己要求实现。
1.3 主要用途
阻塞队列接口具体实现类主要用于生产者-消费者队列,前面说了BlockingQueue
本身是线程安全的,因此支持多个生产者及多个消费者。
该模式将“找出需要完成的工作”和“执行工作”分离开来,并且将待完成工作放入队列中以便随后处理,而不需要找出后立即处理。阻塞队列实现生产者-消费者模式可以简化开发模式,消除了生产者和消费者的相互依赖。生产者和消费者可以做到相互隔离,生产者不需要知道消费者产生过程,只需要将生产的数据放入队列中;同样消费者也不需要知道生产者是谁,来源何地,只需要从队列中取数据即可。线程池就是最好的生产者-消费者的利用。线程池原理解析
put()方法作用等同于生产者,take()方法作用等同于消费者。
take()会一直阻塞直到有可用数据可消费,如果生产者不能尽快生产数据使消费者保持工作,则消费者会一直处于等待状态,只到有工作可做;put()方法会一直阻塞直到队列不为空,如果生产者生产速度远超消费者消费速度,则数据会在队列中累积起来,不断的消耗内存,如果队列满了,生产者需要等待,直到有可用空间可容纳数据。
1.4 主要实现类
下图为BlockingQueue主要实现类
其中ArrarBlockingQueue和LinkedBlockingQueue是先进先出(FIFO)队列,二者分别于ArrayList和linkedList类似,但是比Vector具有更好的并发性。PriorityBlockingQueue是一个按优先级排序的队列,可以自己定义元素的顺序。
其中SynchronousQueue也是BlockingQueue的实现类,严格意义将,该接口不能算队列,因为本身不保存任何的元素,而是维护一组线程,线程用于处理元素的添加及移除。比如小明(生产者)将书放入只能容纳一本书的书架,小红(消费者)马上去取书,效果等通过小明直接将书给小红,降低数据从生产者到消费者的延迟。
来源:CSDN
作者:每周一小记
链接:https://blog.csdn.net/weixin_37690143/article/details/104055571