为了账号安全,请及时绑定邮箱和手机立即绑定

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

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

繁华开满天机 2019-04-04 22:15:48
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是否完全是线程安全的
查看完整描述

3 回答

?
慕码人2483693

TA贡献1860条经验 获得超9个赞

BlockingQueue 的javadoc(LinkedBlockingQueue的超类)声明:

BlockingQueue实现是线程安全的。所有排队方法都使用内部锁或其他形式的并发控制以原子方式实现其效果

“原子地”一词意味着如果两个操作(例如a put和a take)同时发生,那么实现将确保它们根据合同行为。效果会仿佛put之前发生get,反之亦然。这也适用于边缘情况,例如具有一个元素的队列示例。

实际上,由于put并且get正在阻塞操作,因此两个操作的相对顺序无关紧要。与offerpolladdremove顺序的事,但你无法控制它。


请注意,上述内容完全基于javadoc所说的内容。假设我已正确解释了javadoc,那么它适用于所有1个 BlockingQueue实现,无论它们是使用一个还是两个锁......或者根本不使用。如果BlockingQueue实现不像上面那样,那就是一个错误!

1 - 正确实现API的所有实现。这应该涵盖所有Java SE类。


查看完整回答
反对 回复 2019-05-15
?
慕丝7291255

TA贡献1859条经验 获得超6个赞

put 实施一个 LinkedBlockingQueue

public void put(E e) throws InterruptedException {
    // some lock and node code

    // the part that matters here
    try {

        while (count.get() == capacity) {
            notFull.await();
        }

        // put the item in the queue.
    } finally {
        // not important here
    }}

基本上,在put调用线程wait中,容量小于最大持续时间。

即使将值放在队列上的线程抓取与take线程不同的锁,它也会等待将其添加到队列中,直到队列未满。

take有一个类似的实现,notEmpty而不是notFull

分享编辑


查看完整回答
反对 回复 2019-05-15
?
慕哥6287543

TA贡献1831条经验 获得超10个赞


经过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;  // first also points to the only item in queue

h.next = h; 

head = first;

E x = first.item;

first.item = null;

return x;

这两个线程的交集是最后指向put线程以及在线程中首先指向的线程。请注意,put thread仅修改last.item并且take thread仅修改first.next。虽然这两个线程修改了同一个对象实例,但它们会修改它的不同成员,并且不会导致任何线程冲突。


查看完整回答
反对 回复 2019-05-15
  • 3 回答
  • 0 关注
  • 940 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信