正确写法

class BoundedBlockingQueue {
Queue<Integer> queue;
int cap = 0;
Lock lock = new ReentrantLock();
Condition notFull = lock.newCondition();
Condition notEmpty = lock.newCondition();
public BoundedBlockingQueue(int capacity) {
queue = new ArrayDeque<>(capacity);
cap = capacity;
}
public void enqueue(int element) throws InterruptedException {
try {
lock.lock();
while (queue.size() == cap) {
notFull.await();
}
queue.offer(element);
notEmpty.signalAll();
} finally {
lock.unlock();
}
return;
}
public int dequeue() throws InterruptedException {
try {
lock.lock();
while (queue.peek()==null) {
notEmpty.await();
}
int res = queue.poll();
notFull.signalAll();
return res;
} finally {
lock.unlock();
}
}
public int size() {
try {
lock.lock();
return queue.size();
} finally {
lock.unlock();
}
}
}
错误写法
class BoundedBlockingQueue {
Queue<Integer> queue;
int cap = 0;
Lock lock = new ReentrantLock();
Condition notFull = lock.newCondition();
Condition notEmpty = lock.newCondition();
public BoundedBlockingQueue(int capacity) {
queue = new ArrayDeque<>(capacity);
cap = capacity;
}
public void enqueue(int element) throws InterruptedException {
try {
lock.lock();
//if 会出大问题
if (queue.size() == cap) {
notFull.await();
}
queue.offer(element);
notEmpty.signalAll();
} finally {
lock.unlock();
}
return;
}
public int dequeue() throws InterruptedException {
try {
lock.lock();
//if 会出大问题
if (queue.peek()==null) {
notEmpty.await();
}
int res = queue.poll();
notFull.signalAll();
return res;
} finally {
lock.unlock();
}
}
public int size() {
try {
lock.lock();
return queue.size();
} finally {
lock.unlock();
}
}
}

先说下 notEmpty.await(); 是在等待notEmpty.signal的通知
通知到了之后,会先抢锁,抢到锁之后,然后在上一次暂停的地方继续运行。
如果用if 判断,假设有两个消费者线程A和B都阻塞在 notEmpty.await() 这里,都在等待队列元素的到来,
此时生产者放入一个元素,并用 signAll 唤醒了所有的消费者线程, 此时假设A抢到了锁,继续执行取出刚刚那个数据,此时队列就为空了,然后释放锁,
然后B抢到了锁,它从上一次wait的地方继续执行,尝试去队列里取数据,但是这个时候队列已经是空的了,但是却并没有去校验。
因此 要么把if 换成while 防止这种情况发生,醒来之后循环再去做判断,这也叫防止虚假唤醒。 别人的signalAll把你给唤醒了,这时候未必满足你的运行条件,如果不去检测状态,可能就违背了你的初衷,你的本意是队列不为空才取数的。
要么就用signal,随机唤醒一个消费者线程,就不会出现刚刚那种情况,因为未被唤醒的线程不会参与竞争锁。
本文探讨了阻塞队列在实现中如何处理虚假唤醒问题。通过分析错误的使用if判断的情况,指出当多个消费者线程被不恰当的signalAll唤醒后,可能会导致线程在队列为空时仍尝试取数据。为了解决这个问题,建议使用while循环进行状态检查,确保只有在队列非空时才执行操作,或者采用signal方法唤醒单个消费者,避免未被唤醒的线程参与锁的竞争。这两种方法可以有效地防止虚假唤醒,保证阻塞队列的正确运行。
4663

被折叠的 条评论
为什么被折叠?



