当Java LinkedBlocking Queue只有一个元素时,如果同时放置和取出会发生什么?

LinkedBlocking Queue有两个锁,一个用于put,一个用于take。当队列的大小为1时,我认为两个线程可以同时锁定和操作队列,这将导致未定义的行为。我错了吗?

// method put:                             // method take:             // put lock                                
// take lock
 putLocK.lockInterruptibly();              takeLock.lockInterruptibly();                      
 ...                                       ...

 while(count.get() == capacity){           while(count.get() == 0){
   notFull.await();                          notEmpty.await();
 }                                         }
 enqueue(node);                            x = dequeue();// method enqueue:                         
 // method dequeue:
  last = last.next = node;                 Node<E> h = head;    
 ...                                       Node<E> first = h.next;
                                           h.next = h;        
                                           head = first;     
                                           E x = first.item;     
                                           first.item = null;   
                                           return x;

当队列中只有一个项目时,显然可以将线程和线程锁定,因此它们将分别在方法入队和出队中执行代码。我的意思是如果线程进入方法出列,在所有指针修改后,不会与enqueue中的代码冲突?

这里的链接说“但是当队列为空时,无法避免争用,因此需要额外的代码来处理这种常见的”边缘“情况”

在Java中,BlockingQueue是否完全是线程安全的


繁华开满天机
浏览 941回答 3
3回答

慕码人2483693

BlockingQueue&nbsp;的javadoc(LinkedBlockingQueue的超类)声明:BlockingQueue实现是线程安全的。所有排队方法都使用内部锁或其他形式的并发控制以原子方式实现其效果。“原子地”一词意味着如果两个操作(例如a&nbsp;put和a&nbsp;take)同时发生,那么实现将确保它们根据合同行为。效果会仿佛在put之前发生get,反之亦然。这也适用于边缘情况,例如具有一个元素的队列示例。实际上,由于put并且get正在阻塞操作,因此两个操作的相对顺序无关紧要。与offer/&nbsp;poll或add/&nbsp;remove顺序做的事,但你无法控制它。请注意,上述内容完全基于javadoc所说的内容。假设我已正确解释了javadoc,那么它适用于所有1个&nbsp;BlockingQueue实现,无论它们是使用一个还是两个锁......或者根本不使用。如果BlockingQueue实现不像上面那样,那就是一个错误!1 - 正确实现API的所有实现。这应该涵盖所有Java SE类。

慕丝7291255

put&nbsp;实施一个&nbsp;LinkedBlockingQueuepublic&nbsp;void&nbsp;put(E&nbsp;e)&nbsp;throws&nbsp;InterruptedException&nbsp;{ &nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;some&nbsp;lock&nbsp;and&nbsp;node&nbsp;code &nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;the&nbsp;part&nbsp;that&nbsp;matters&nbsp;here &nbsp;&nbsp;&nbsp;&nbsp;try&nbsp;{ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;while&nbsp;(count.get()&nbsp;==&nbsp;capacity)&nbsp;{ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;notFull.await(); &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;put&nbsp;the&nbsp;item&nbsp;in&nbsp;the&nbsp;queue. &nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;finally&nbsp;{ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;not&nbsp;important&nbsp;here &nbsp;&nbsp;&nbsp;&nbsp;}}基本上,在put调用线程wait中,容量小于最大持续时间。即使将值放在队列上的线程抓取与take线程不同的锁,它也会等待将其添加到队列中,直到队列未满。take有一个类似的实现,notEmpty而不是notFull。分享编辑

慕哥6287543

经过2天的搜索,我终于明白了...当队列中只有一个项目时,根据LinkedBlocking Queue的设计,实际上有两个节点:虚拟头和真实项目(同时最后指向它)。确实,put thread和take thread都可以获得锁定,但是它们会修改队列的不同部分。Put线程会调用last = last.next = node; // last points to the only item in queue拿线程会打电话Node<E> h = head;Node<E> first = h.next;&nbsp; // first also points to the only item in queueh.next = h;&nbsp;head = first;E x = first.item;first.item = null;return x;这两个线程的交集是最后指向put线程以及在线程中首先指向的线程。请注意,put thread仅修改last.item并且take thread仅修改first.next。虽然这两个线程修改了同一个对象实例,但它们会修改它的不同成员,并且不会导致任何线程冲突。
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

Java