Why does the iterator.hasNext not work with BlockingQueue?
Solution 1
1) Is this bad design, or wrong expectation?
Wrong expectations since it would otherwise violate the contract of Iterator which on Iterator.next()
says: Throws: NoSuchElementException - iteration has no more elements.
If next()
would block the exception would never be thrown.
2) Is there a way to use the blocking methods
Yes, for instance by extending the class and overriding the next
and hasNext
methods to use blocking routines instead. Note that hasNext
would need to always return true
in this case - which again violates the contract.
Solution 2
Just don't use iterators with Queues. Use peek()
or poll()
instead or take()
if it's a 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
is an Iterable
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.
Solution 3
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.
In any case the LinkedBlockingQueue
javadoc has this to say
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.
VGDIV
Updated on June 27, 2022Comments
-
VGDIV almost 2 years
I was trying to use the iterator methods on a BlockingQueue and discovered that hasNext() is non-blocking - i.e. it will not wait until more elements are added and will instead return false when there are no elements.
So here are the questions :
- Is this bad design, or wrong expectation?
- Is there a way to use the blocking methods of the BLockingQueue with its parent Collection class methods (e.g. 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)
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.
-
planetjones almost 13 yearsWell yes it will but the question was how could it work via the java.util.Iterator - i think we can agree the BlockingQueue.take() should really be used here.
-
VGDIV almost 13 years>>A Queue is an Iterable because it is a Collection and hence needs to provide an iterator() method, but that shouldn't ever be used<< Couldn't this be an atleast partial case of bad design in Java?
-
VGDIV almost 13 yearsWhat if we consider itr.next() in isolation i.e. - would good design principles dictate that BlockingQueue implementation ensure blocking if there were no objects on the queue? However I see your point that hasNext() contract would be violated with or without this.
-
Sean Patrick Floyd almost 13 years@Varun yes and no. It's a valid use case to add all remaining members of a Queue to a different collection, so the iterator() makes sense there, but just not for normal everyday use.
-
Johan Sjöberg almost 13 yearsI think not. Iterator is used to view a snapshot of a list. If implementing it blocking it could potentially block forever which in your case might be useful, but doesn't serve the purpose it was included for.
-
WestCoastProjects almost 10 yearsThis is the right answer. I was going down the wrong path myself - considering to use an iterator() for queue. But that is not the way to go.