阻塞队列的实现
1. 有界队列
队列中的元素个数有限, 队列为空时, get 操作或阻塞, 队列满时, add 操作会阻塞。
常见的有界队列时 ArrayBlockingQueue
和 LinkedBlockingQueue
.
有界队列的写法, 一般是有一把全局大锁同步读写操作,notFull 和 notEmpty 两个 Condition 作为条件谓词进行空满检验。 ArrayBlockingQueue
和 LinkedBlockingQueue
也是这样实现的, 不同的是前者只有一把大锁, 读写没有分离; 后者有2把锁,效率会高一些
2. 无界队列
队列中元素个数不限制,因此不会产生 get, put 的阻塞操作. 常见的无界非阻塞队列为 ConcurrentLinkedQueue
ConcurrentLinkedQueue
因为是无解的, 所以 offer 操作永远返回 true, take 操作如果没有元素返回 null。 ConcurrentLinkedQueue
的增删节点是使用 cas 操作指针进行的, 满足框架
class ConcurrentLinkedQueue {
Unsafe unsafe = ...
Node heda, tail;
// 插入
boolean offer(Node newNode) {
for (;;) {
Node tail = this.tail; // 先获取一下 tail
// 看 tail 是否发生了变化, CAS 更新 tail 引用
if(unsafe.cas(tailoffset, tail, newNode)) {
tail.next = newNode; // 连上 next 引用
break;
}
}
}
Node take() {
// ...
}
static {
headOffset = unsafe.getOffset(this.head) ... // 得到属性偏移量
tailOffset = unsafe.getOffset(this.tail) ... // 得到属性偏移量
}
}
此外, ConcurrentLinkedQueue
并不是安全的遍历, 其中并没有一个 AtomicInteger 的 size 属性, 要获取 size 就是遍历一下, 而因为是无锁遍历, 所以即使发生节点增删, 也不能感知到, 因此只能获取一个近似值
3. jdk 有界队列分析
```java
class LinkedBlockingQueue<E>{
private final int capacity; // 队列容量
// 当前队列内元素个数 AtomicInteger
private final AtomicInteger count = new AtomicInteger();
Node<E> head,last;
// 读锁
private final ReentrantLock takeLock = new ReentrantLock();
// 读锁下的非空谓词
private final Condition notEmpty = takeLock.newCondition();
// 写锁
private final ReentrantLock putLock = new ReentrantLock();
// 写锁下的非满谓词
private final Condition notFull = putLock.newCondition();
/** 读锁下: 条件谓词进行通知 */
private void signalNotEmpty() {
this.takeLock.lock();
try {
notEmpty.signal();
} finally {
this.takeLock.unlock();
}
}
/** 写锁下: 条件谓词进行通知 */
private void signalNotFull() {
this.putLock.lock();
try {
notFull.signal();
} finally {
this.putLock.unlock();
}
}
/** 写操作 */
public void put(E e){
int c = -1;
Node<E> node = new Node<E>(e);
this.putLock.lock();
try {
// 条件的 while 检测, 为了防止被错误唤醒
while (this.count.get() == capacity) { // count 已满检验
notFull.await(); // notFull 条件谓词等待
}
enqueue(node); // 入队操作
int c = this.count.getAndIncrement(); // count + 1
if (c + 1 < capacity)
// 因为是 signal, 而不是 signalAll, 所以要通知其它同类 put 操作 notFull
notFull.signal();
} finally {
this.putLock.unlock();
}
// 最重要:put 后获取 take 锁通知 notEmpty
if (c == 0)
signalNotEmpty();
}
/** 读操作 */
public E take() throws InterruptedException {
E x;
int c = -1;
final AtomicInteger count = this.count;
this.takeLock.lockInterruptibly(); // 上锁
try {
while (this.count.get() == 0) { // siz = 0的检验
notEmpty.await(); // 非空条件谓词等待
}
x = dequeue();
c = this.count.getAndDecrement(); // count 增加
if (c > 1)
notEmpty.signal(); // 通知其它 take 操作 notEmpty
} finally {
this.takeLock.unlock();
}
// 最重要:take 后获取 put 锁通知 notFull
if (c == capacity)
signalNotFull();
return x;
}
// 无锁下的出队逻辑
private E dequeue() {
Node<E> h = head, first = h.next;
h.next = h; // help GC next指向自己
head = first;
E x = first.item;
first.item = null;
return x;
}
}