6

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

所以这里有问题:

  1. 这是糟糕的设计,还是错误的期望?
  2. 有没有办法将 BLockingQueue 的阻塞方法与它的父 Collection 类方法一起使用(例如,如果某个方法期望一个集合,我可以传递一个阻塞队列并希望它的处理将等到队列有更多元素)

这是一个示例代码块

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();
        }
        }
    }

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

4

5 回答 5

12

只是不要将迭代器与队列一起使用。使用peek()orpoll()代替,或者take()如果它是BlockingQueue

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();
}

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

于 2011-05-27T09:06:13.603 回答
3

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

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

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

是的,例如通过扩展类并覆盖nextandhasNext方法来使用阻塞例程。请注意,在这种情况下hasNext需要始终返回true- 这再次违反了合同。

于 2011-05-27T08:54:39.343 回答
3

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

无论如何,LinkedBlockingQueuejavadoc都有这个说法

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.
于 2011-05-27T09:15:51.883 回答
0

我认为在某些情况下,有一个Iterable谁的iterator()意志阻止可能是合理的,尽管有一个单独的BlockingIterator会是愚蠢的。这样做的原因是因为这样可以避免您使用增强的for循环,在某些情况下,这可以使您的代码更清晰。(如果在您的特定情况下无法做到这一点,请不要这样做。)

for(Request request:requests) process(request);

但是,迭代器仍然没有终止条件!一旦队列对新项目关闭并且元素用完,迭代器应该终止。

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

于 2013-11-21T02:46:35.433 回答
-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());
        }
    }
于 2011-05-27T08:58:59.817 回答