简体   繁体   English

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

[英]Why does the iterator.hasNext not work with BlockingQueue?

I was trying to use the iterator methods on a BlockingQueue and discovered that hasNext() is non-blocking - ie it will not wait until more elements are added and will instead return false when there are no elements.我试图在 BlockingQueue 上使用迭代器方法,发现 hasNext() 是非阻塞的——即它不会等到添加更多元素,而是在没有元素时返回 false。

So here are the questions:所以这里有问题:

  1. Is this bad design, or wrong expectation?这是糟糕的设计,还是错误的期望?
  2. Is there a way to use the blocking methods of the BLockingQueue with its parent Collection class methods (eg if some method were expecting a collection, can I pass a blocking queue and hope that its processing will wait until the Queue has more elements)有没有办法将 BLockingQueue 的阻塞方法与它的父 Collection class 方法一起使用(例如,如果某些方法需要一个集合,我可以传递一个阻塞队列并希望它的处理会等到队列有更多元素)

Here is a sample code block这是一个示例代码块

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

This Code only prints the iteration once at the most.此代码最多只打印一次迭代。

Just don't use iterators with Queues.只是不要将迭代器与队列一起使用。 Use peek() or poll() instead or take() if it's a BlockingQueue :如果它是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();
}

A Queue is anIterable because it is a Collection and hence needs to provide an iterator() method, but that shouldn't ever be used, or you shouldn't be using a Queue in the first place.Queue是一个Iterable ,因为它是一个Collection ,因此需要提供一个iterator()方法,但不应该使用它,或者你不应该首先使用 Queue。

1) Is this bad design, or wrong expectation? 1)这是糟糕的设计,还是错误的期望?

Wrong expectations since it would otherwise violate the contract of Iterator which on Iterator.next() says: Throws: NoSuchElementException - iteration has no more elements.错误的期望,因为否则它会违反Iterator.next()上的Iterator合同: Throws: NoSuchElementException - iteration has no more elements. If next() would block the exception would never be thrown.如果next()将阻止异常将永远不会被抛出。

2) Is there a way to use the blocking methods 2)有没有办法使用阻塞方法

Yes, for instance by extending the class and overriding the next and hasNext methods to use blocking routines instead.是的,例如通过扩展 class 并覆盖nexthasNext方法来使用阻塞例程。 Note that hasNext would need to always return true in this case - which again violates the contract.请注意,在这种情况下hasNext需要始终返回true - 这再次违反了合同。

if an iterator blocked on hasNext then the iteration would never finish unless you explicitly broke out of it, this would be quite a strange design.如果一个迭代器在hasNext上阻塞,那么除非你明确地打破它,否则迭代将永远不会完成,这将是一个非常奇怪的设计。

In any case the LinkedBlockingQueue javadoc has this to say在任何情况下, 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.

I think that it may be reasonable under certain circumstances to have an Iterable whose iterator() will block, although having a seperate BlockingIterator would be foolish.我认为在某些情况下拥有一个Iterableiterator()将阻塞可能是合理的,尽管拥有一个单独的BlockingIterator是愚蠢的。 The reason for this is because that lests you use an enhanced for loop, which can,in some cases, make your code cleaner.这样做的原因是因为这样可以避免您使用增强for循环,在某些情况下,这可以使您的代码更清晰。 (If it would not accomplish that in your particular circumstance, do not do this at all.) (如果在您的特定情况下无法做到这一点,请不要这样做。)

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

However, the iterator is still not free from a termination condition, The iterator should terminate once the queue has been closed to new items, and runs out of elements.但是,迭代器仍然没有终止条件,一旦队列对新项目关闭并且元素用完,迭代器应该终止。

The issue still remains, though, that if the loop was already blocking on the iterator's next() method, the only way to exit if the queue is closed is to throw an exception, which the surrounding code would need to handle correctly, If you choose to do this, make sure you explain very clearly and precisely, how your implementation works in the javadoc comments.但是,问题仍然存在,如果循环已经阻塞在迭代器的next()方法上,如果队列关闭,退出的唯一方法是抛出异常,周围的代码需要正确处理,如果你选择这样做,请确保您在 javadoc 注释中非常清楚准确地解释您的实现是如何工作的。

The Iterator for LinkedBlockingQueue has this as its hasNext implementation: LinkedBlockingQueue 的迭代器将其作为 hasNext 实现:

  private Node<E> current;

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

so this will only work per call.所以这只适用于每次通话。 You can wrap the method in a while(true) loop if you want to wait for elements and use the standard java Iterator idiom:如果要等待元素并使用标准 java 迭代器习惯用法,可以将方法包装在 while(true) 循环中:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM