Почему iterator.hasNext не работает с BlockingQueue? - PullRequest
6 голосов
/ 27 мая 2011

Я пытался использовать методы итератора в BlockingQueue и обнаружил, что hasNext () неблокирует - то есть он не будет ждать, пока будет добавлено больше элементов, и вместо этого вернет false, когда элементов нет.

Итак, вот вопросы:

  1. Это плохой дизайн или неправильное ожидание?
  2. Есть ли способ использовать методы блокировки BLockingQueue с егометоды родительского класса Collection (например, если какой-то метод ожидал коллекцию, могу ли я пропустить очередь блокировки и надеяться, что ее обработка будет ждать, пока в очереди будет больше элементов)

Вот пример кода1011 *

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 ]

10 голосов
/ 27 мая 2011

Только не используйте итераторы с очередями.Используйте peek() или poll() вместо или 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();
}

A Queue - это Iterable, потому что это Collection и, следовательно, необходимо предоставить метод iterator(), но его никогда не следует использоватьили вы не должны использовать очередь в первую очередь.

4 голосов
/ 27 мая 2011

1) Это плохой дизайн или неправильное ожидание?

Неправильные ожидания, поскольку в противном случае он нарушил бы контракт Итератор , который на Iterator.next() говорит: Throws: NoSuchElementException - iteration has no more elements. Если next() заблокирует исключение, оно никогда не будет выдано.

2) Есть ли способ использовать методы блокировки

Да, например, путем расширения класса и переопределения методов next и hasNext для использования вместо них процедур блокировки. Обратите внимание, что hasNext должен всегда возвращать true в этом случае, что снова нарушает контракт.

3 голосов
/ 27 мая 2011

если итератор заблокирован на 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.
0 голосов
/ 21 ноября 2013

Я думаю, что при определенных обстоятельствах было бы разумно иметь Iterable, чей iterator() будет блокировать, хотя иметь отдельный BlockingIterator было бы глупо. Причина этого в том, что это позволяет вам использовать расширенный цикл for, который в некоторых случаях может сделать ваш код чище. (Если этого не произойдет в ваших конкретных обстоятельствах, не делайте этого вообще.)

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

Однако итератор все еще не свободен от условия завершения! Итератор должен завершиться, как только очередь будет закрыта для новых элементов, и исчерпает элементы.

Однако проблема все еще остается в том, что, если цикл уже блокировался в методе next() итератора, единственный способ выйти, если очередь закрыта, - вызвать исключение, которое окружающий код должен был бы обработать правильно. , Если вы решите сделать это, убедитесь, что вы очень четко и точно объясните, как ваша реализация работает в комментариях javadoc.

0 голосов
/ 27 мая 2011

Итератор для LinkedBlockingQueue имеет это в качестве реализации hasNext:

  private Node<E> current;

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

, так что это будет работать только на вызов. Вы можете заключить метод в цикл while (true), если хотите дождаться элементов и использовать стандартную итерацию java Iterator:

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