阻塞队列之LinkedBlockingQueue
LinkedBlockingQueue也是有界队列,可以设置容量大小。如果没有设置,默认容量是整数最大值。
public LinkedBlockingQueue() {
// 默认容量为整数最大值
this(Integer.MAX_VALUE);
}
// 指定容量大小
public LinkedBlockingQueue(int capacity) {
if (capacity <= 0) throw new IllegalArgumentException();
this.capacity = capacity;
last = head = new Node<E>(null);
}
LinkedBlockingQueue的基本属性
// 链表的节点类
static class Node<E> {
E item;
Node<E> next;
Node(E x) { item = x; }
}
// 链表的容量
private final int capacity;
// 链表中元素个数
private final AtomicInteger count = new AtomicInteger();
// 链表的头节点
transient Node<E> head;
// 链表的尾节点
private transient Node<E> last;
// 消费者的锁
private final ReentrantLock takeLock = new ReentrantLock();
// 消费者的Condition
private final Condition notEmpty = takeLock.newCondition();
// 生产者的锁
private final ReentrantLock putLock = new ReentrantLock();
// 生产者的Condition
private final Condition notFull = putLock.newCondition();
LinkedBlockingQueue存放元素的方法
add方法
public boolean add(E e) {
if (offer(e))
return true;
else
throw new IllegalStateException("Queue full");
}
offer非阻塞方法
- 判断入参是否为null,如果为null,抛出异常
- 判断链表是否已满,如果已满,返回false
- 将数据封装成Node节点
- 加锁
- 再次判断链表是否已满,如果已满,返回false
- 如果未满,在链表尾部添加数据
- 判断当前链表是否已满,如果未满,唤醒生产者
- 释放锁
- 唤醒消费者
public boolean offer(E e) {
if (e == null) throw new NullPointerException();
final AtomicInteger count = this.count;
if (count.get() == capacity)
// 链表中元素个数达到链表容量,返回false
return false;
int c = -1;
Node<E> node = new Node<E>(e);
final ReentrantLock putLock = this.putLock;
putLock.lock();
try {
if (count.get() < capacity) {
// 往链表中添加元素
enqueue(node);
// getAndIncrement是先返回值,再+1
// 拿到count的值,并且将count+1
c = count.getAndIncrement();
if (c + 1 < capacity)
// 当前链表中元素个数小于链表容量
// signal方法只会将条件队列头节点放到同步队列中
// 要唤醒同步队列中的节点,还是在unlock方法中
notFull.signal();
}
} finally {
// 唤醒生产者同步队列中的线程
putLock.unlock();
}
if (c == 0)
// 此处的c表示元素放入前,链表中元素个数
// 进入这里说明链表中有元素了,唤醒消费者
signalNotEmpty();
return c >= 0;
}
private void enqueue(Node<E> node) {
last = last.next = node;
}
private void signalNotEmpty() {
final ReentrantLock takeLock = this.takeLock;
takeLock.lock();
try {
// signal方法将消费者条件队列中节点放到同步队列中
notEmpty.signal();
} finally {
// 唤醒消费者同步队列中的线程
takeLock.unlock();
}
}
offer阻塞方法
- 判断入参是否为null,如果为null,抛出异常
- 判断链表是否已满,如果已满,返回false
- 将数据封装成Node节点
- 加锁
- while循环判断链表是否已满,如果已满,判断方法等待时间是否已过
- 如果未过,线程阻塞一段时间,并且释放锁
- 线程被唤醒,尝试获取锁,获取锁后,重复5、6、7步
- 如果链表未满,在链表尾部添加数据
- 判断当前链表是否已满,如果未满,唤醒生产者
- 释放锁
- 唤醒消费者
public boolean offer(E e, long timeout, TimeUnit unit)
throws InterruptedException {
if (e == null) throw new NullPointerException();
long nanos = unit.toNanos(timeout);
int c = -1;
final ReentrantLock putLock = this.putLock;
final AtomicInteger count = this.count;
putLock.lockInterruptibly();
try {
while (count.get() == capacity) {
if (nanos <= 0)
return false;
nanos = notFull.awaitNanos(nanos);
}
enqueue(new Node<E>(e));
c = count.getAndIncrement();
if (c + 1 < capacity)
notFull.signal();
} finally {
putLock.unlock();
}
if (c == 0)
signalNotEmpty();
return true;
}
put方法
- 判断入参是否为null,如果为null,抛出异常
- 判断链表是否已满,如果已满,返回false
- 将数据封装成Node节点
- 加锁
- while循环判断链表是否已满,如果已满,线程阻塞,并且释放锁
- 线程被唤醒,尝试获取锁,获取锁后,重复5、6步
- 如果链表未满,在链表尾部添加数据
- 判断当前链表是否已满,如果未满,唤醒生产者
- 释放锁
- 唤醒消费者
public void put(E e) throws InterruptedException {
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();
}
if (c == 0)
signalNotEmpty();
}
LinkedBlockingQueue获取元素的方法
remove方法
public E remove() {
E x = poll();
if (x != null)
return x;
else
throw new NoSuchElementException();
}
poll无参方法
- 判断链表是否为空,如果为空,返回null
- 加锁
- 判断链表是否为空,如果为空,返回null
- 链表不为空,获取链表头部节点
- 判断链表是否不为空,如果不为空,唤醒消费者
- 释放锁
- 判断取走数据前,链表是否已满,如果已满,唤醒生产者
public E poll() {
final AtomicInteger count = this.count;
if (count.get() == 0)
// 链表中没有元素,返回null
return null;
E x = null;
int c = -1;
final ReentrantLock takeLock = this.takeLock;
takeLock.lock();
try {
if (count.get() > 0) {
// 链表中有元素,获取元素
x = dequeue();
c = count.getAndDecrement();
if (c > 1)
// 及时唤醒消费者
notEmpty.signal();
}
} finally {
takeLock.unlock();
}
if (c == capacity)
// 进入这里说明获取数据前,链表是满的
// 现在取走了一个数据,链表中有空闲位置,可以唤醒生产者
signalNotFull();
return x;
}
private E dequeue() {
Node<E> h = head;
Node<E> first = h.next;
h.next = h;
head = first;
E x = first.item;
first.item = null;
return x;
}
private void signalNotFull() {
final ReentrantLock putLock = this.putLock;
putLock.lock();
try {
notFull.signal();
} finally {
putLock.unlock();
}
}
poll有参方法
- 判断链表是否为空,如果为空,返回null
- 加锁
- while循环判断链表是否为空,如果为空,判断方法等待时间是否已过
- 如果时间未过,线程阻塞一段时间,并且释放锁
- 线程被唤醒,尝试获取锁,获取锁后,重复执行3、4、5步
- 链表不为空,获取链表头部数据
- 判断链表是否不为空,如果不为空,唤醒消费者
- 释放锁
- 判断取走数据前,链表是否已满,如果已满,唤醒生产者
public E poll(long timeout, TimeUnit unit) throws InterruptedException {
E x = null;
int c = -1;
long nanos = unit.toNanos(timeout);
final AtomicInteger count = this.count;
final ReentrantLock takeLock = this.takeLock;
// 竞争锁失败处于挂起状态时,可以响应中断
takeLock.lockInterruptibly();
try {
while (count.get() == 0) {
if (nanos <= 0)
return null;
nanos = notEmpty.awaitNanos(nanos);
}
x = dequeue();
c = count.getAndDecrement();
if (c > 1)
notEmpty.signal();
} finally {
takeLock.unlock();
}
if (c == capacity)
signalNotFull();
return x;
}
take方法
- 判断链表是否为空,如果为空,返回null
- 加锁
- while循环判断链表是否为空,如果为空,线程阻塞,并且释放锁
- 线程被唤醒,尝试获取锁,获取锁后,重复执行3、4步
- 链表不为空,获取链表头部数据
- 判断链表是否不为空,如果不为空,唤醒消费者
- 释放锁
- 判断取走数据前,链表是否已满,如果已满,唤醒生产者
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;
}