为什么iterator.hasNext不能与BlockingQueue一起使用?

时间:2011-05-27 08:43:57

标签: java multithreading collections java.util.concurrent

我试图在BlockingQueue上使用迭代器方法,并发现hasNext()是非阻塞的 - 即它不会等到添加更多元素,而是在没有元素时返回false。

以下是问题:

  1. 这是一个糟糕的设计,还是错误的 期望?
  2. 有没有办法使用阻止 BLockingQueue的方法 它的父集合类方法 (例如,如果某种方法是期待的话 一个集合,我可以通过阻止 排队并希望其处理 将等到队列有更多 元素)
  3. 以下是示例代码块

    public class SomeContainer{
         public static void main(String[] args){
            BlockingQueue bq = new LinkedBlockingQueue();
            SomeContainer h = new SomeContainer();
            Producer p = new Producer(bq);
            Consumer c = new Consumer(bq);
            p.produce();
            c.consume();
        }
    
        static class Producer{
            BlockingQueue q;
            public Producer(BlockingQueue q) {
                this.q = q;
            }
    
            void produce(){
            new Thread(){
                public void run() {
                for(int i=0; i<10; i++){
                    for(int j=0;j<10; j++){
                        q.add(i+" - "+j);
                    }
                    try {
                        Thread.sleep(30000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                };
            }.start();
            }
        }
    
    
        static class Consumer{
             BlockingQueue q;
    
             public Consumer(BlockingQueue q) {
                 this.q = q;
             }
    
            void consume() {
                new Thread() {
                    public void run() {
                        Iterator itr = q.iterator();
                        while (itr.hasNext())
                            System.out.println(itr.next());
                    }
                }.start();
            }
            }
        }
    

    此代码仅打印最多一次迭代。

5 个答案:

答案 0 :(得分:10)

只是不要将迭代器与队列一起使用。如果是BlockingQueue,请使用peek()poll()take()

void consume() {
    new Thread() {
        @Override
        public void run() {
            Object value;
            // actually, when using a BlockingQueue,
            // take() would be better than poll()
            while ((value=q.poll())!=null)
                System.out.println(value);
        }
    }.start();
}

QueueIterable,因为它是Collection,因此需要提供iterator()方法,但不应该使用,或者你不应该首先要使用队列。

答案 1 :(得分:4)

  

1)这是糟糕的设计还是错误的期望?

错误的期望,否则会违反Iterator.next() Throws: NoSuchElementException - iteration has no more elements.所说的Iterator合同:next() 如果next阻止,则永远不会抛出异常。

  

2)有没有办法使用阻止方法

是的,例如通过扩展类并重写hasNexthasNext方法来改为使用阻塞例程。请注意,true在这种情况下需要始终返回{{1}} - 这又违反了合同。

答案 2 :(得分:3)

如果迭代器在hasNext上被阻塞,那么迭代将永远不会完成,除非你明确地突破它,这将是一个非常奇怪的设计。

在任何情况下,LinkedBlockingQueue javadoc都有这个说法

Returns an iterator over the elements in this queue in proper sequence. 
The returned <tt>Iterator</tt> is a "weakly consistent" iterator that will 
never throw {@link ConcurrentModificationException}, and guarantees to 
traverse elements as they existed upon construction of the iterator, and 
may (but is not guaranteed to) reflect any modifications subsequent to 
construction.

答案 3 :(得分:0)

我认为在某些情况下,Iterable iterator()阻止BlockingIterator可能是合理的,尽管单独for会是愚蠢的。这样做的原因是因为这会让您使用增强的for(Request request:requests) process(request); 循环,在某些情况下,这可以使您的代码更清晰。 (如果在特定情况下无法实现,请不要这样做。)

next()

然而,迭代器仍然没有终止条件!一旦队列关闭到新项目,用完了元素,迭代器应该终止。

但问题仍然存在,如果循环已经在迭代器的{{1}}方法上阻塞,那么在队列关闭时退出的唯一方法是抛出一个异常,周围的代码需要这个异常正确处理,如果你选择这样做,请确保你非常清楚,准确地解释你的实现如何在javadoc注释中工作。

答案 4 :(得分:-1)

LinkedBlockingQueue的迭代器将其作为hasNext实现:

  private Node<E> current;

   public boolean hasNext() {
        return current != null;
    }

所以这只适用于每次通话。如果要等待元素并使用标准的java Iterator惯用语,可以将方法包装在while(true)循环中:

    while (true) {     
       if(itr.hasNext()) {
          System.out.println(itr.next());
        }
    }