java为什么LinkedBlockingQueue的put()中有while循环
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();
}
为什么会有一个while循环
所有的推杆线都被锁死了
当等待的线程持有putLock时,没有线程可以增加“count”
# 1 楼答案
当LinkedBlockingQueue的容量已满时,循环的函数(*)将阻塞名为put方法的线程。当另一个线程调用take(或poll)方法时,队列中将有新元素的空间,take方法将发出
notFull
条件的信号,等待的线程将被唤醒,并可以将项目放入队列(*)循环的条件是确保没有发生虚假唤醒
https://en.wikipedia.org/wiki/Spurious_wakeup
# 2 楼答案
await
有一个基本属性(它通过synchronized
应用于内在锁定,也使用Object.wait
),您必须理解:当你调用
await
时,你是在释放锁这个Condition
与\关联。没有办法绕过它,否则,没有人可以获得锁,使条件得到满足,并对其调用signal
当等待的线程收到信号时,它不会立即拿回锁。这是不可能的,因为调用
signal
的线程仍然拥有它。相反,接收方将尝试重新获取锁,与调用lockInterruptibly()
没有太大区别但这个线程不一定是唯一一个试图获取锁的线程。它甚至不必是第一个。另一个线程可能在发出信号并等待
lockInterruptibly()
的锁之前到达了put
。因此,即使锁是公平的(哪些锁通常不是),有信号的线程也没有优先级。即使给了发信号的线程优先权,也可能有多个线程因为不同的原因被发信号因此,到达
put
的另一个线程可以在发出信号的线程之前获得锁,发现有空间,并存储元素,而不必担心信号。然后,当发出信号的线程获得锁时,该条件不再满足。因此,有信号的线程永远不能仅仅因为收到了信号就依赖于条件的有效性,因此必须重新检查条件,如果没有满足,就再次调用await
这使得检查循环中的条件成为使用} interface 中所述,对于使用内部监视器的情况,也可以使用^{} ,只是为了完整性。换句话说,这甚至不是特定于特定API的
await
的标准习惯用法,如the ^{由于无论如何都必须在循环中预先检查和重新检查该条件,因此该规范甚至允许虚假唤醒,即线程从等待操作返回而没有实际接收到信号的事件。这可能会简化某些平台的锁实现,同时不会改变锁的使用方式
必须强调的是,当持有多个锁时,只有与该条件相关的锁被释放
# 3 楼答案
@Holder的回答是正确的,但我想补充关于以下代码和问题的更多细节
while循环是这个代码模式的关键部分,它确保当线程从
notFull
上的信号中被唤醒时,它确保另一个线程没有首先到达那里,并重新填充缓冲区重要的一点是要认识到
notFull
被定义为putLock
上的一个条件:当线程调用
notFull.await()
时,它将解锁putLock
,这意味着多个线程可以同时运行notFull.await()
。只有在调用notFull.signal()
(或signalAll()
)后,线程才会尝试重新获取锁如果线程A是
BLOCKED
试图获取putLock
,而线程B是WAITING
在notFull
上,则会发生争用条件。如果线程C从队列中删除了某些内容并发出信号notFull
,线程B将从等待队列中取出并放入putLock
上的阻塞队列中,但不幸的是,它将位于已被阻塞的线程a后面。因此,一旦putLock
被解锁,线程A将获取putLock
,并将某些内容放入队列中,再次填充它。当线程B最终获取putLock
时,它需要再次测试,看看在放入(并溢出)队列之前是否还有可用的空间。这就是为什么while
是必要的正如@Holder所提到的,while循环的第二个原因是为了防止在某些线程架构下,当一个条件被人工通知时,可能会发生虚假的唤醒。例如,在某些架构下,由于操作系统的限制,any条件上的信号表示all条件