求对ArrayBlockingQueue中一段源码的解释

发布于 2022-09-04 23:15:31 字数 402 浏览 21 评论 0

为什么take()方法在判断队列中元素个数是否为0的时候使用了while循环,而不是if;

clipboard.png

我认为,如果notEmpty.await()方法既然苏醒返回了 ,那么此线程肯定拿到了lock。

而苏醒的原因也是因为put()方法放入了新的元素,而其他线程无法拿到锁,自然无法取走元素,那么此时对于拿到锁的线程来说count肯定不为0了,应该放心的执行 dequeue()获取元素就可以了。
不知道作者使用了while是何意呢?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

身边 2022-09-11 23:15:32

先看 Condition.awit() 的源码:

public final void await() throws InterruptedException {
    if (Thread.interrupted())
        throw new InterruptedException();
    Node node = addConditionWaiter();
    // 1. 释放当前线程所占用的 lock
    int savedState = fullyRelease(node);
    int interruptMode = 0;
      // 2. 将当前线程放入同步队列
    while (!isOnSyncQueue(node)) {
        LockSupport.park(this);
        if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
            break;
    }
    // 3. 阻塞等待直到获取到 lock
    if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
        interruptMode = REINTERRUPT;
    if (node.nextWaiter != null)
        unlinkCancelledWaiters();
    if (interruptMode != 0)
        reportInterruptAfterWait(interruptMode);
}

await 方法核心做了 3 件事情:

  1. 释放当前线程所占用的 lock
  2. 将当前线程放入同步队列
  3. 阻塞等待直到获取到 lock

所以 notEmpty.await() 方法既然苏醒返回了 ,那么此线程肯定拿到了 lock 这句话是正确的。

此时,我们假设 ArrayBlockingQueuetake 方法使用的是 if

public E take() throws InterruptedException {
    final ReentrantLock lock = this.lock;
    lock.lockInterruptibly();
    try {
        if (count == 0)
            notEmpty.await();
        return dequeue();
    } finally {
        lock.unlock();
    }
}
  1. 如果此时 count == 0 时,有 2 个线程 A 和 B 都执行了 notEmpty.await(),且 A 和 B 都已经执行到了 acquireQueued(node, savedState)(即都处于 自旋等待 中,这时 A 和 B 都处于阻塞中)
  2. 然后线程 C 往 ArrayBlockingQueue 中添加了一个元素,接着执行 notEmtpy.signal() 唤醒一个线程
  3. 假设唤醒的是线程 A,那么 A 就继续向下执行 dequeue(),然后线程 A 释放锁(这种情况没有问题)
  4. 如果此时 B 在某种特殊情况下被唤醒(spurious wakeup:https://docs.oracle.com/en/ja...)并获得锁,因为使用的不是 while,所以此时不会再重新判断条件,从而继续向下执行 dequeue() 导致出错
南…巷孤猫 2022-09-11 23:15:32
public ArrayBlockingQueue(int capacity, boolean fair) {
    if (capacity <= 0)
        throw new IllegalArgumentException();
    this.items = new Object[capacity];
    lock = new ReentrantLock(fair);
    notEmpty = lock.newCondition();    //notEmpty 就是这个锁的条件
    notFull =  lock.newCondition();
}

所以在你这段代码里当前队列为空时(count==0),调用了notEmpty.await(),这段代码对锁是有影响的,实际上底层上已经释放了锁,只是这个方法保证了被唤醒时一定又能够拿回锁(当有元素放入队列会调用notEmpty.signal()进行唤醒),那为什么需要使用while呢?因为insert后lock.unlock,未必notEmpty.await()立即被唤醒,可能之前插入一个线程运行remove方法

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文