常用方法
类注释
- 基于链表的阻塞队列
- FIFO特性
- 链表默认为Integer的最大值
数据结构
/**
* 链表
*/
static class Node<E> {
E item;
/**
* 为null时,表示当前节点是队尾
*/
Node<E> next;
Node(E x) { item = x; }
}
/** 未指定的话,容量为Integer最大值 */
private final int capacity;
/** 当前元素数量 */
private final AtomicInteger count = new AtomicInteger();
/**
* 头节点
*/
transient Node<E> head;
/**
* 尾结点
*/
private transient Node<E> last;
/** take, poll 持有的锁*/
private final ReentrantLock takeLock = new ReentrantLock();
/** Wait queue for waiting takes */
private final Condition notEmpty = takeLock.newCondition();
/** put, offer 持有的锁 */
private final ReentrantLock putLock = new ReentrantLock();
/** Wait queue for waiting puts */
private final Condition notFull = putLock.newCondition();
锁分为take锁和put锁,是为了两个操作可以同时进行,互不影响
操作方法
初始化操作
- 由于是链表结构,理论上可以是无限大,所以容量大小不会和数组那样需要提前申请空间而影响性能。初始容量太小会导致队列很快放满
PUT 方法
// 添加元素e到队列尾部
// 新增元素,空间满等待,空间不满直接成功
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();
}
//队列中里有一个元素,唤醒一个take等待线程
if (c == 0)
signalNotEmpty();
}
//入队,把元素直接放在队尾
private void enqueue(Node<E> node) {
// assert putLock.isHeldByCurrentThread();
// assert last.next == null;
last = last.next = node;
}
take方法
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
c = count.getAndDecrement();
if (c > 1)
notEmpty.signal();
} finally {
takeLock.unlock();
}
if (c == capacity)
signalNotFull();
return x;
}
private E dequeue() {
// assert takeLock.isHeldByCurrentThread();
// assert head.item == null;
Node<E> h = head;
Node<E> first = h.next;
h.next = h; // help GC
head = first;
E x = first.item;
first.item = null;
return x;
}
这个工具类可以用到各种阻塞场景中