weird CopyOnWriteArraySet error

D

Daisy

I'm getting a java.util.NoSuchElementException at high loads using
java.util.concurrent.CopyOnWriteArraySet. I have one guess why and
would like to hear if it makes sense. Could this error occur because
two threads call the same instance?

For example, thread A executes i.hasNext() which returns true. Then
thread B runs and happens to start executing at i.next(). When thread
A runs again, it will execute the i.next() but B has already advanced
the iterator to the end of the list.

I'm using CopyOnWriteArraySet to avoid synchronizing. Do I have to
sync to avoid this issue?

Thanks for the opinions!

public class DistributionSet extends CopyOnWriteArraySet {

private Iterator i;

public void enqueue( Object message ) {

for ( i = this.iterator( ) ; i.hasNext( ) ; ) {

// .next() call is throwing an error - why? either:
// copy hasn't completed or .hasNext() has a different
count, or some listener was removed
EventListener listener = ( EventListener ) i.next( );
listener.eventObserved( message ) ;

}
}

public boolean add( EventListener consumer ) {
super.add( consumer );
return true;

}
}
 
P

Patricia Shanahan

Daisy said:
I'm getting a java.util.NoSuchElementException at high loads using
java.util.concurrent.CopyOnWriteArraySet. I have one guess why and
would like to hear if it makes sense. Could this error occur because
two threads call the same instance?

For example, thread A executes i.hasNext() which returns true. Then
thread B runs and happens to start executing at i.next(). When thread
A runs again, it will execute the i.next() but B has already advanced
the iterator to the end of the list.

I'm using CopyOnWriteArraySet to avoid synchronizing. Do I have to
sync to avoid this issue?

Thanks for the opinions!

public class DistributionSet extends CopyOnWriteArraySet {

private Iterator i;

public void enqueue( Object message ) {

for ( i = this.iterator( ) ; i.hasNext( ) ; ) {

// .next() call is throwing an error - why? either:
// copy hasn't completed or .hasNext() has a different
count, or some listener was removed
EventListener listener = ( EventListener ) i.next( );
listener.eventObserved( message ) ;

}
}

public boolean add( EventListener consumer ) {
super.add( consumer );
return true;

}
}

If enqueue can be called in multiple threads, then there is a risk of
NoSuchElementException. For example:

Suppose thread A calls enqueue, and enqueue assigns to i the Iterator
reference iterator() returned, and i.hasNext() returns true. Now A gets
interrupted, thread B starts running, and thread B calls enqueue.

Thread B assigns to i the Iterator reference that its iterator() call
returned. At that point, the thread A Iterator becomes unreachable.
Thread B completes its enqueue call, leaving i referencing an iterator
that returned false from i.hasNext().

Now thread A gets control back, but i references the Iterator that
thread B was using. The thread A next call fails.

If the uses of the shared Iterator are finely interleaved, as they could
be on a dual processor, you could get other problems such as not passing
a particular message to some listeners.

However, it is a problem you created, by choosing to make i an instance
field forcing multiple threads to share the iterator reference. Why is
that necessary?

If i were local, each enqueue activation would be able to remember its
own Iterator reference.

Patricia
 
D

Daisy

Thank you Patricia for the excellent response.

I originally had i as a local variable, but moved it to be an instance
variable to make the code faster. I'll move it back being a local
variable.

Thanks
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,774
Messages
2,569,598
Members
45,160
Latest member
CollinStri
Top