【问题标题】:Why does the iterator.hasNext not work with BlockingQueue?为什么 iterator.hasNext 不能与 BlockingQueue 一起使用?
【发布时间】:2011-09-03 05:40:39
【问题描述】:

我试图在 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();
        }
        }
    }

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

【问题讨论】:

    标签: java multithreading collections java.util.concurrent


    【解决方案1】:

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

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

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

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

    【讨论】:

    • 如果我们孤立地考虑 itr.next() 会怎样——良好的设计原则会规定 BlockingQueue 实现在队列上没有对象的情况下确保阻塞吗?但是我明白你的观点,无论有没有这个,hasNext() 合同都会被违反。
    • 我认为不是。 Iterator 用于查看列表的快照。如果实现它的阻塞,它可能会永远阻塞,这在您的情况下可能有用,但不能达到包含它的目的。
    【解决方案2】:

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

    【讨论】:

    • 是的,它会,但问题是它如何通过 java.util.Iterator 工作 - 我认为我们可以同意 BlockingQueue.take() 应该在这里真正使用。
    【解决方案3】:

    只是不要将迭代器与队列一起使用。如果是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();
    }
    

    Queue 是一个 Iterable,因为它是一个 Collection,因此需要提供一个 iterator() 方法,但不应该使用它,或者你不应该在第一名。

    【讨论】:

    • >>队列是一个 Iterable,因为它是一个集合,因此需要提供一个 iterator() 方法,但永远不应该使用它
    • @Varun 是和否。将 Queue 的所有剩余成员添加到不同的集合是一个有效的用例,因此 iterator() 在那里有意义,但不适用于日常使用。
    • 这是正确的答案。我自己走错了路 - 考虑使用 iterator() 队列。但这不是要走的路。
    【解决方案4】:

    如果一个迭代器在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.
    

    【讨论】:

      【解决方案5】:

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

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

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

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2018-03-09
        • 2021-06-14
        • 2012-10-09
        • 2020-03-18
        • 2017-11-21
        • 2019-04-11
        • 2012-09-19
        • 2013-12-30
        相关资源
        最近更新 更多