Reputation: 5067
The other days we found a rather awkward exception in the logs of our application in this piece of code:
final LIST currentSinks = this.sinks;
if (null != currentSinks && !currentSinks.isEmpty())
{
for (final DataSink<? super T> sink : currentSinks)// **the exception is thrown here**
{
sink.updated(oldValue, newValue);
}
}
where LIST is
public abstract class AbstractDataSource<T, LIST extends Collection<DataSink<? super T>>>
The implementation used is ArrayList. It just throws NoSuchElementException. To be honest I don't really understand why. Whatever I tried just had the expected behavior: no iteration through the for.
The stack trace's first line:
java.util.NoSuchElementException caught here: java.util.ArrayList$Itr.next(ArrayList.java:834)
Any hint or explanation would be highly appreciated.
EDIT Probably a good starting point would be to isolate the behavior in a UT. Any thoughts on this?
Upvotes: 2
Views: 2284
Reputation: 2793
Well, I will make the effort o sum up what was discussed in the comments. Thanks everyone and fell free to edit this answer.
The error description feels like a concurrency error, so our theory is:
probably another thread removed an element between the Iterator's
hasNext()
andnext()
call, that happens behind the scenes in thefor(:)
loop (thanks Steve!).
So, lets test our theory:
1) First of all, list
type is ArrayList
and from ArrayList docs we have:
ArrayList is not synchronized, so if you need multi-threaded access, consider using
List l = Collections.synchronizedList(new ArrayList(...));
2) Then we check source code to find references to NoSuchElementException
: no occurrence found on java.util.ArrayList.java
, but five occurrences in java.util.AbstractList.java
, which the most interesting is:
// under: public ListIterator<E> listIterator(final int index) {
// return new ListIterator<E>() {
// line 1040:
/**
* Retrieves the next object from the list.
*
* @return The next object.
* @throws NoSuchElementException if there are no
* more objects to retrieve.
* @throws ConcurrentModificationException if the
* list has been modified elsewhere.
*/
public E next()
{
if (position == size)
throw new NoSuchElementException();
position++;
return i.next();
}
Back to our theory we can say that:
ArrayList
is not synchronized (1) and there is a point where a NoSuchElementException
is thrown by an iterator.next()
before any concurrent modification check (2).Hope it helps you solve your problem!
Upvotes: 2