阻塞队列有两种阻塞
队列是空的,拿的时候拿不到,阻塞了
队列是满的,放的时候放不进,阻塞了
阻塞队列在JDK里面有一个专门的接口BlockingQueue
并不是所有的方法都是阻塞的方法
插入元素和拿取元素成对出现的两种操作
非阻塞类的方法
add()插入元素,往满的队列插入时元素时插不进会抛出异常
remove()拿取元素,从空的队列拿元素时拿不出来会抛出异常
非阻塞类的方法
offer()插入元素,往满的队列插入时元素时插不进会返回false
poll()拿取元素,从空的队列拿元素时拿不出来会返回null
阻塞类的方法
put()插入元素,往满的队列插入时元素时插不进线程会被阻塞
take()拿取元素,从空的队列拿元素时拿不出来线程会被阻塞
生产者消费者模式
生产者生产数据,消费者消费数据
如果生产者生产的很快,消费者消费速度慢,那么生产者要等消费者消费完再继续生产
如果是消费者的速度快,大部分时间消费者是在等生产者
为了平衡生产者和消费者之间能力不均衡的情况,在生产者和消费者之间放一个容器,生产者生产后放入容器,只管放入,消费者从容器拿数据,不关心数据从哪里来,平衡了生产者和消费者性能不均衡的问题,生产者和消费者结耦
阻塞队列用在生产者消费者问题中好处:
- 生产者消费者分离结耦
- 平衡生产者消费者之间性能不均衡问题
常用阻塞队列
ArrayBlockingQueue:一个由数组结构组成的有界阻塞队列。
LinkedBlockingQueue:一个由链表结构组成的有界阻塞队列。
PriorityBlockingQueue:一个支持优先级排序的无界阻塞队列。
DelayQueue:一个使用优先级队列实现的无界阻塞队列。
SynchronousQueue:一个不存储元素的阻塞队列。
LinkedTransferQueue:一个由链表结构组成的无界阻塞队列。
LinkedBlockingDeque:一个由链表结构组成的双向阻塞队列。
有界与无界:
有界:队列长度是有限的,满了以后生产者就会被阻塞
无界:可以不听的放东西,不会被阻塞
比如ArrayBlockingQueue的构造函数必须传入一个容量,要指明当前ArrayBlockingQueue容量是多大
public ArrayBlockingQueue(int capacity) {
this(capacity, false);
}
查看无界的支持优先级的阻塞队列PriorityBlockingQueue
发现也有支持传入int,但这里是初始化容量大小,没有限定最大大小
public PriorityBlockingQueue(int initialCapacity) {
this(initialCapacity, null);
}
在一般的队列中是根据放入元素的前后顺序进行排队的,支持优先级的含义是在放入元素时可以先比较下,放入时根据某种方式进行比较,比较结果谁大或者谁小就放到队列前头,允许放入元素进行排序,默认是按照自然排序的。允许传入比较器
public PriorityBlockingQueue(int initialCapacity,
Comparator<? super E> comparator) {
if (initialCapacity < 1)
throw new IllegalArgumentException();
this.lock = new ReentrantLock();
this.notEmpty = lock.newCondition();
this.comparator = comparator;
this.queue = new Object[initialCapacity];
}
综上:有界规定了最大容量,无界没有规定最大容量
当然,在实际的开发过程中,没法做到真正的无界,因为资源总是有限的,尽量使用有界控制容量大小
无界队列拿取还是会阻塞的
DelayQueue:一个使用优先级队列实现的无界阻塞队列
支持元素的延时获取
public class DelayQueue<E extends Delayed> extends AbstractQueue<E>
implements BlockingQueue<E> {
......
}
DelayQueue除了是个泛型还有个约束,要实现Delayed这个接口
public interface Delayed extends Comparable<Delayed> {
/**
* Returns the remaining delay associated with this object, in the
* given time unit.
*
* @param unit the time unit
* @return the remaining delay; zero or negative values indicate
* that the delay has already elapsed
*/
long getDelay(TimeUnit unit);
}
getDelay是返回当前元素剩余时长
谁的剩余时间短谁就排在前头,先被取出来
阻塞多了一种约束:及时有元素,元素的剩余时间没有到,也拿不出来
用处:单机缓存系统,时间设置过期
SynchronousQueue:一个不存储元素的阻塞队列
内部没有任何容器,当我们使用生产者线程使用一个put操作往SynchronousQueue阻塞队列里面放东西的时候,队端必须有一个消费者使用take方法把这个元素拿走,否则生产者不能继续添加元素的,数据的直接传递
生产者直接给消费者的话就产生耦合了,生产者就必须要知道消费者是谁了,使用SynchronousQueue后生产者往队列里面放,不用关系消费者有几个消费者是谁。
LinkedTransferQueue:一个由链表结构组成的无界阻塞队列
多了一个尝试传输
生产者往队列里面放元素时,发现正好有消费者在等待拿元素,它就直接把元素交给消费者,不再往队列里面放了
public void transfer(E e) throws InterruptedException {
if (xfer(e, true, SYNC, 0) != null) {
Thread.interrupted(); // failure possible only due to interrupt
throw new InterruptedException();
}
}
还有tryTransfer,会试探下有没有消费者拿,如果不试探直接给,没有消费者接受的话,transfer方法会一直阻塞在这个位置,必须等有消费者拿走才能返回
tryTransfer会尝试下有没有消费者,没有消费者消费就放入队列里面
public boolean tryTransfer(E e) {
return xfer(e, true, NOW, 0) == null;
}
LinkedBlockingDeque:一个由链表结构组成的双向阻塞队列
一般的队列一个入口一个出口,双向同时作为出口和入口 (减少一个竞争),Deque双端
它的方法会多,firt,last,从头从尾取(放)
以下三个平常可能用的多一些:
ArrayBlockingQueue:一个由数组结构组成的有界阻塞队列。
LinkedBlockingQueue:一个由链表结构组成的有界阻塞队列。
DelayQueue:一个使用优先级队列实现的无界阻塞队列。