为什么在LinkedBlockingQueue的put()中有一个while循环 [英] why is there a while loop in put() of LinkedBlockingQueue
问题描述
public void put(E e)抛出InterruptedException {
if(e == null)throw new NullPointerException();
int c = -1;
Node< E>节点=新的节点E(e);
最后的ReentrantLock putLock = this.putLock;
最终AtomicInteger计数= this.count;
putLock.lockInterruptible();
try {
while(count.get()== Capacity){
notFull.await();
}
入队(节点);
c = count.getAndIncrement();
如果(c + 1 <容量)
notFull.signal();
}最终{
putLock.unlock();
}
if(c == 0)
signalNotEmpty();
}
为什么会有while循环?
所有put线程都被putLock封闭。
当等待线程持有putLock时,没有线程可以增加'count'。
有一个基本属性 await
(适用于通过已同步
,并同时使用 Object.wait
),您必须了解:
当调用 await
时,您正在释放锁此条件
与¹相关联。无法绕开它,否则,没有人可以获取锁,满足条件并对其调用 signal
。
当等待线程发信号时,它不会立即获得锁。那是不可能的,因为调用 signal
的线程仍然拥有它。相反,接收方将尝试重新获取锁,与调用 lockInterruptible()
没什么不同。
但是此线程不一定是试图获取锁的唯一线程。它甚至不必是第一个。在发信号之前,另一个线程可能已经到达 put
并等待 lockInterruptible()
的锁定。因此,即使锁是公平的(通常不锁),发出信号的线程也没有优先级。即使您给了发出信号的线程优先级,也可能出于不同的原因发出多个信号。
因此,另一个线程到达了 put
可以在发出信号的线程之前获得锁,发现有空间,并且可以存储元素而不必担心信号。然后,到发出信号的线程获取锁时,该条件不再满足。因此,发出信号的线程永远不会仅仅依赖条件的有效性,因为它收到了信号,因此必须重新检查条件,如果不满足,则必须再次调用 await
。 / p>
这使在循环中检查条件成为使用 await
的标准习惯用法,如 条件
界面,以及 Object.wait
(仅出于完整性考虑)。换句话说,这甚至不是特定于API的。
由于必须对条件进行预检查和循环检查,因此规范甚至允许虚假唤醒,即线程从等待操作返回而实际上未接收到信号的事件。这可以简化某些平台的锁实现,而无需更改必须使用锁的方式。
¹重要的是要强调指出,当持有多个锁时,仅与条件关联的锁被释放。
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();
}
why is there a while loop?
All the putting thread is shut out by putLock.
No thread can increase 'count' when the waiting thread is holding the putLock.
There is a fundamental property of await
(which applies to the intrinsic locking via synchronized
and using Object.wait
as well), you have to understand:
When you invoke await
, you are releasing the lock this Condition
is associated with¹. There is no way around it, as otherwise, no-one could acquire the lock, make the condition fulfilled, and invoke signal
on it.
When your waiting thread gets signaled, it does not get the lock back immediately. That would not be possible, as the thread which invoked signal
still owns it. Instead, the receiver will try to re-acquire the lock, not much different to calling lockInterruptibly()
.
But this thread is not necessarily the only thread trying to acquire the lock. It doesn’t even have to be the first one. Another thread could have arrived at put
before the signalling and waiting for the lock at lockInterruptibly()
. So even if the lock was fair (which locks usually are not), the signaled thread had no precedence. Even if you gave signaled threads precedence, there could be multiple threads being signaled for different reasons.
So another thread arriving at put
could get the lock before the signaled thread, find that there is space, and store the element without ever bothering with signals. Then, by the time the signaled thread acquired the lock, the condition is not fulfilled anymore. So a signaled thread can never rely on the validity of the condition just because it received a signal and therefore has to re-check the condition and invoke await
again if not fulfilled.
This makes checking the condition in a loop the standard idiom of using await
, as documented in the Condition
interface, as well as Object.wait
for the case of using the intrinsic monitor, just for completeness. In other words, this is not even specific to a particular API.
Since the condition has to be pre-checked and re-checked in a loop anyway, the specification even allows for spurious wakeups, the event of a thread returning from the wait operation without actually receiving a signal. This may simplify lock implementations of certain platforms, while not changing the way a lock has to be used.
¹ It’s important to emphasize that when holding multiple locks, only the lock associated with the condition is released.
这篇关于为什么在LinkedBlockingQueue的put()中有一个while循环的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!