Warum verwendet die take()-Methode eine while-Schleife anstelle von if, wenn sie beurteilt, ob die Anzahl der Elemente in der Warteschlange 0 ist?
我认为,如果notEmpty.await()方法既然苏醒返回了 ,那么此线程肯定拿到了lock。
Der Grund für das Aufwachen liegt auch darin, dass die put()-Methode neue Elemente einfügt und andere Threads die Sperre nicht erhalten können, sodass sie die Elemente zu diesem Zeitpunkt natürlich nicht entfernen können count ist definitiv nicht 0. Sie können gerne dequeue() ausführen, um Elemente abzurufen. Ich weiß nicht, was der Autor mit „while“ meint?
“notEmpty.await()方法既然苏醒返回了 ,那么此线程肯定拿到了 lock” 这句话是正确的。
假设你写的代码是:
可以明确的一点就是我们需要保证在
return dequeue()
之前需要满足的一个条件是count != 0
。我们假设 线程A 此时拿到了 lock,那么 线程A 的notEmpty.await()
此时便会停止阻塞,准备向下执行return dequeue()
。但是假设在竞争激烈的条件下,线程A 拿到 lock 之后,准备执行下一条 JVM 指令的时候,线程B 此时抢占了 lock,然后继续向下执行return dequeue()
,刚好使得 count 变为了 0;而此时因为写的只是if(count == 0)
,那么线程 A 在拿到 lock 之后,还是会继续向下执行return dequeue()
,从而导致了错误。关于为什么
wait()
、await()
这样当条件不满足时就阻塞的方法一定要在 while 循环中使用,可以参考《Effective Java》第二版第 69 条(244 页)。所以在你这段代码里当前队列为空时(count==0),调用了
notEmpty.await()
,这段代码对锁是有影响的,实际上底层上已经释放了锁,只是这个方法保证了被唤醒时一定又能够拿回锁(当有元素放入队列会调用notEmpty.signal()
进行唤醒),那为什么需要使用while呢?因为insert后lock.unlock
,未必notEmpty.await()
立即被唤醒,可能之前插入一个线程运行remove方法