概述
LinkedBlockingQueue
内部由单链表实现
,只能从head取元素,从tail添加元素。添加元素和获取元素都有独立的锁,也就是说LinkedBlockingQueue是读写分离的
,读写操作可以并行执行。LinkedBlockingQueue采用可重入锁ReentrantLock
来保证在并发情况下的线程安全。
构造器
LinkedBlockingQueue
一共有三个构造器,分别是无参构造器、可以指定容量的构造器、可以穿入一个容器的构造器。如果在创建实例的时候调用的是无参构造器,LinkedBlockingQueue
的默认容量是Integer.MAX_VALUE
,这样做很可能会导致队列还没有满,但是内存却已经满了的情况(内存溢出)。
public LinkedBlockingQueue(); //设置容量为Integer.MAX
public LinkedBlockingQueue(int capacity); //设置指定容量
public LinkedBlockingQueue(Collection<? extends E> c); //穿入一个容器,如果调用该构造器,容量默认也是Integer.MAX_VALUE
LinkedBlockingQueue常用操作
取数据
take()
:首选。当队列为空时阻塞
poll()
:弹出队顶元素,队列为空时,返回空
peek()
:和poll烈性,返回队队顶元素,但顶元素不弹出。队列为空时返回null
remove(Object o)
:移除某个元素,队列为空时抛出异常。成功移除返回true
添加数据
put()
:首选。队满是阻塞
offer()
:队满时返回false
判断队列是否为空
size()
方法会遍历整个队列,时间复杂度为O(n),所以最好选用isEmtpy
put元素原理
基本过程:
1.判断元素是否为null,为null抛出异常
2.加锁(可中断锁)
3.判断队列长度是否到达容量,如果到达一直等待
4.如果没有队满,enqueue()在队尾加入元素
5.队列长度加1,此时如果队列还没有满,调用signal唤醒其他堵塞队列
if (e == null) throw new NullPointerException();
int c = -1;
Node<E> node = new Node<E>(e);
final ReentrantLock putLock = this.putLock;
final AtomicInteger count = this.count;
putLock.lockInterruptibly();
try {
while (count.get() == capacity) {
notFull.await();
}
enqueue(node);
c = count.getAndIncrement();
if (c + 1 < capacity)
notFull.signal();
} finally {
putLock.unlock();
}
take元素原理
基本过程:
1.加锁(依旧是ReentrantLock),注意这里的锁和写入是不同的两把锁
2.判断队列是否为空,如果为空就一直等待
3.通过dequeue方法取得数据
3.取走元素后队列是否为空,如果不为空唤醒其他等待中的队列
public E take() throws InterruptedException {
E x;
int c = -1;
final AtomicInteger count = this.count;
final ReentrantLock takeLock = this.takeLock;
takeLock.lockInterruptibly();
try {
while (count.get() == 0) {
notEmpty.await();
}
x = dequeue();
c = count.getAndDecrement();
if (c > 1)
notEmpty.signal();
} finally {
takeLock.unlock();
}
if (c == capacity)
signalNotFull();
return x;
}
enqueue()和dequeue()方法实现都比较简单,无非就是将元素添加到队尾,从队顶取走元素,感兴趣的朋友可以自己去看一下,这里就不粘贴了。