BlockingQueue
BlockingQueue
接口定义了很多插入和删除的办法,这里总结梳理如下:
增加
- 容器满了,抛异样
add(e) - 返回是否胜利
offer(e) - 容器满了,阻塞
put(e) - 容器满了,期待超时退出
offer(e, time, unit)
删除
- 容器没有元素,抛异样
remove() - 返回是否胜利
remove(obj)
poll() - 容器没有元素,阻塞
take() - 容器没有元素,期待超时退出
poll(time, unit)
其余
- 获取队头元素,但不出容器,没有则抛异样
element() - 获取队头元素,但不出容器,没有不抛异样
peek()
总结
有界阻塞队列
ArrayBlockingQueue
- 数组实现的有界阻塞队列;
- 队列依照先进先出准则对元素进行排列;
- 默认不保障线程偏心拜访队列;
- 所谓偏心拜访队列,是指依照阻塞线程的程序拜访队列。
LinkedBlockingQueue
- 链表实现的有界阻塞队列;
- FIFO
- 未设置容器大小,默认为
Integer.MAX_VALUE
SynchronousQueue
- 不存储元素的有界阻塞队列
- 每一个 put 操作,必须对应另外一个线程的 take 操作
- 用 队列 实现偏心拜访;用 栈实现不偏心拜访
LinkedBlockingDequeue
- 双向链表实现的有界阻塞队列
- 双向链表指的是:可从队头、队尾 插入或删除元素
- 未设置容器大小,默认为
Integer.MAX_VALUE
共性
put
操作在容器满时,会阻塞take
操作在容器没有元素时,会阻塞
无界阻塞队列
PriorityBlockingQueue
- 基于 堆 实现的具备 优先级 的无界阻塞队列
- 默认依照天然排序升序排列
- 可通过元素的外部排序,或内部排序自定义程序
DelayQueue
- 外部基于
PriorityQueue
实现反对延时获取元素的无界阻塞队列 - 队列内元素必须实现
Delayed
接口
LinkedTransferQueue
- 链表实现的无界阻塞队列
- 与其余阻塞队列不同的是,多提供了
transfer
,tryTransfer
办法 transfer
会阻塞到有消费者从队列中生产元素tryTransfer
不带工夫的办法,会间接返回;带工夫的办法,会依据工夫阻塞到有消费者生产
总结
put
,take
均不阻塞
阻塞队列的道与术
下面讲了阻塞队列如何应用,然而那仅仅是术。只有理解了道(阻塞队列的设计),咱们才能够在即便是换了语言的状况下,也能模拟 JAVA
阻塞队列的设计思路,本人编写阻塞队列。
问题一:抉择适合的数据结构
数据结构,选数组,还是链表都能够。只有分明数组,链表各自的优缺点,并进行抉择即可。
思考到须要高速存取,不心愿有内存碎片。因而,抉择数组会绝对适合。在思考到容器的重复使用,咱们就须要 2 个指针,寄存以后消费者生产到哪个地位,生产者最初生产的元素在哪个地位。
其实以上形容的是循环数组的构造。
问题二:容器满了,生产者如何实现不在生产;容器没元素,消费者如何实现不再生产
这里用 伪代码 代码形容该问题。
public void producer() {while(数组长度 == 容器内的元素个数) {生产者休眠}
}
public void consumer() {while(容器内的元素个数 == 0) {消费者休眠}
}
问题三:生产者应该在什么时候唤醒消费者;消费者应该在什么时候唤醒生产者
将上诉代码略微革新下。咱们只须要在 producer
办法被调用后,就能够告诉消费者生产了
public void producer() {while(数组长度 == 容器内的元素个数) {生产者休眠}
生产
唤醒消费者
}
public void consumer() {while(容器内的元素个数 == 0) {消费者休眠}
生产
唤醒生产者
}
问题四:如何爱护共享资源
对于共享资源的爱护,那只能应用锁来爱护了
public void producer() {
加锁
while(数组长度 == 容器内的元素个数) {生产者休眠}
生产
唤醒消费者
开释锁
}
public void consumer() {
加锁
while(容器内的元素个数 == 0) {消费者休眠}
生产
唤醒生产者
开释锁
}
总结
上诉的代码其实是十分经典的多生产者与多消费者的代码。这边做的革新,仅仅是应用循环数组作为存储容器。
参考
- 聊聊 Java 中的并发队列中 有界队列和无界队列的区别
- 《Java 并发编程的艺术》