【问题标题】:ArrayBlockingQueue and add vs put vs capacityArrayBlockingQueue 和 add vs put vs capacity
【发布时间】:2011-12-04 03:13:24
【问题描述】:

来自ArrayBlockingQueueArrayBlockingQueue的Javadoc:

添加

公共布尔加法(E e)

Inserts the specified element at the tail of this queue if it is possible 
to do so immediately without exceeding the queue's capacity, returning true 
upon success and throwing an IllegalStateException if this queue is full.

我一直将这句话(if it is possible to do so immediattely)解释如下:

如果队列有空闲容量,则插入将成功。如果没有空白空间,则不会成功。

但我的理解在这里是错误的。

在一个简单的例子中,我决定使用ArrayBlockingQueue,例如20 个元素(小队列)并有一个线程在做:

queue.take()

尽管队列几乎是空的,但另一个线程没有通过add 方法将元素添加到队列中。

我也通过调试验证了它。

一旦我将queue.add(element) 的调用替换为queue.put(element),该元素确实已添加到队列中。

那么这些方法有什么不同呢?

还有什么其他原因(除了容量)不能添加?


更新:

public class ConnectionListener implements Observer {

  public static BlockingQueue<ConnectionObject> queueConnections = new   ArrayBlockingQueue<ConnectionObject>(10);

  @Override
  public void update(Observable arg0, Object arg1) {
      ConnectionObject con = ((ConnectionObject)arg1);
      queueConnections.add(con);
  }

}  

ConnectionObject 只是 String 值的持有者。

public class ConnectionObject {
  private String user;  
  private String ip;
   //etc  
}

消费者:

public class ConnectionTreeUpdater extends Thread {  
  @Override
  public void run() {
    while(true){  
    try {  
    final ConnectionObject con = ConnectionListener.queueConnections.take();

如果我使用add,则不会引发异常,但不会将元素添加到队列中。

只是一个想法:也许由于消费者正在队列中“等待”,如果某些内部管家无法添加元素,则不会添加它并且不会引发异常。可能是这种情况。

否则我无法理解为什么没有异常并且使用put 代码有效。

putadd 的使用方式是否不同?

【问题讨论】:

  • 我怀疑您正在捕获并忽略add() 引发的异常,但没有看到您的代码,这只是猜测。您需要发布一个小代码示例来展示您遇到的问题。
  • 其实add里面也没有异常。我刚刚做了queue.add,代码立即返回,没有添加元素,也没有异常
  • @user384706:我们能否看到一个完整且可重现的测试用例来演示此行为(add() 不会引发异常,但也不会将元素添加到队列中)。
  • 不,它没有。它要么将其添加到队列中,要么引发异常。这是仅有的两种可能的结果。
  • @Brian: 用一些代码更新了帖子

标签: java multithreading data-structures concurrency queue


【解决方案1】:

其实很简单:

  • 如果队列未满,两种方法都成功;
  • 如果队列已满,add() 将失败并出现异常,而 put() 会阻塞。

我认为上面的文档很清楚。如果您不同意,并且想要第二个意见,您可以查看ArrayBlockingQueue 的源代码:

public boolean add(E e) {
    if (offer(e))
        return true;
    else
        throw new IllegalStateException("Queue full");
}

public boolean offer(E e) {
    if (e == null) throw new NullPointerException();
    final ReentrantLock lock = this.lock;
    lock.lock();
    try {
        if (count == items.length)
            return false;
        else {
            insert(e);
            return true;
        }
    } finally {
        lock.unlock();
    }
}

public void put(E e) throws InterruptedException {
    if (e == null) throw new NullPointerException();
    final E[] items = this.items;
    final ReentrantLock lock = this.lock;
    lock.lockInterruptibly();
    try {
        try {
            while (count == items.length)
                notFull.await();
        } catch (InterruptedException ie) {
            notFull.signal(); // propagate to non-interrupted thread
            throw ie;
        }
        insert(e);
    } finally {
        lock.unlock();
    }
}

【讨论】:

  • If you find the documentation ambiguous, you can verify this by looking at the source code - 实现细节和文档之间存在差异,所以总的来说我对这个建议并不满意。如果文档不清楚(尽管我认为在这种情况下很清楚),我认为打开一个错误更明智(对于 Java 没有这样做,但对于 msdn 不止一次 [虽然 win32 api 文档是一团糟;-)])
  • @Voo:我明白你的意思。但是,对于有意义的错误报告,必须有一个错误(或有理由相信存在错误)。在这种情况下,文档和代码都非常清楚地说明了同一件事(无论如何在我看来)。
  • 我同意在这种情况下文档足够清晰(好吧,代码显然做了正确的事情TM)。如果文档真的不清楚,仅仅查看实现并假设它永远不会改变将是一个坏主意。为文档创建一个错误以查看这是否只是一个诚实的疏忽还是出于充分的理由未定义是 imo 更好的行动方案(显然首先要谷歌搜索;))
  • @Voo - 虽然我在某种程度上同意你的观点,但如果你怀疑存在错误,你可以编写一个可重复的测试用例来演示它,然后提交报告。 OP 在被询问后似乎无法提供此信息,因此此时告诉他们查看源代码并不是一个糟糕的建议。怀疑每天有数百万开发人员使用的稳定、旧代码中存在错误,这简直是糟糕的逻辑和糟糕工程师的标志。 Jeff(SO 的联合创始人)在codinghorror.com/blog/2008/03/… 中对此进行了概述
  • @BrianRoach 我并没有说那里有错误,如果文档没有指定或不清楚,那么查看实现并不是一个好主意。因此,使用“查看它执行以下操作的 src 代码”来回答有关函数行为的问题可能会导致未来的有趣问题。例子? C# 字符串散列函数没有文档记录,并且 DID 实际上会随着版本的变化而变化,这会导致未来出现有趣的问题。编辑:OP 改变了他的帖子,因为这里是写的,所以上下文发生了一些变化。
【解决方案2】:

调试问题的一个更重要的部分是编写一个测试用例,以确保您认为正在发生的事情确实正在发生。这要么证明要么反驳你的理论。

下面的测试用例表明您使用的方法的行为与文档(您引用的)完全一致:

public static void main(String[] args) {

    final ArrayBlockingQueue<Integer> myQueue =
            new ArrayBlockingQueue<Integer>(10);


    Thread t1 = new Thread(new Runnable() {

        public void run()
        {
            int i = 0;
            while (true)
            {
               try
                {
                    myQueue.add(i);
                    System.out.println("Added to queue! value: " + 
                                        i + 
                                        " size: " + myQueue.size());
                    i++;
                }
                catch (Exception e)
                {
                    System.out.println("add() threw exception, size: " +
                                        myQueue.size());
                    try
                    {
                        Thread.sleep(1000);
                    }
                    catch (InterruptedException ex)
                    {
                        Logger.getLogger(Main.class.getName()).log(Level.SEVERE, 
                                                                   null, ex);
                    }
                }
            }
        }

    });

    Thread t2 = new Thread(new Runnable() {

        public void run()
        {
            while (true)
            {
                try
                {
                    Integer i = myQueue.take();
                    System.out.println("Took a off the queue! value: " + 
                                        i + 
                                       " size: " + myQueue.size());
                    Thread.sleep(100);
                }
                catch (InterruptedException ex)
                {
                    Logger.getLogger(Main.class.getName()).log(Level.SEVERE, 
                                                               null, ex);
                }
            }
        }
    });

    t1.start();
    t2.start();

}

【讨论】:

    猜你喜欢
    • 2014-08-23
    • 1970-01-01
    • 2018-08-01
    • 2021-07-06
    • 2013-10-25
    • 1970-01-01
    • 2011-08-29
    • 2013-07-17
    • 1970-01-01
    相关资源
    最近更新 更多