Other answers have identified the problem:
The iterators for synchronized collections are not synchronized. In fact, they are simply the iterators returned by the collection objects inside the wrapper classes.
Many collection classes (including ArrayList
) use a fail-fast mechanism to detect concurrent modifications during iteration. This behavior is clearly documented in the javadocs for the respective classes. This is what you are seeing.
Not all collection classes do this. For example, many of the java.util.Concurrent...
collection classes allow concurrent modification during iteration, but relax the semantics of the iteration sequence so that the results of the modifications may or may not be apparent in the objects returned by the iterator.
The javadoc for the Collections.synchronizedList()
explains how to synchronize the iterator. Basically you do this:
List list = Collections.synchronizedList(new ArrayList());
...
synchronized (list) {
Iterator i = list.iterator(); // Must be in synchronized block
while (i.hasNext())
foo(i.next());
}
(Aside: normally it is not safe to assume that doing something like this would work. In theory, the synchronized list could use a private lock object, and the synchronized
statement would not lock out concurrent modifications. However the javadocs say that this is what to do in this case ... so it is safe.)
The problem with doing that is that locking the collection creates a potential concurrency bottleneck. The alternative to is to use a copy-on-write data structure that internally makes a copy of the relevant parts of the collection. This approach means that an iterator sees sees a snapshot of the collection. Modifications may be made to the collection concurrent with an iteration, but the iterator does not see them. The problem with copy-on-write is that modifications are potentially a lot more expensive.
Ultimately, you need to balance the characteristics and costs of the different collection types wrt concurrent modification versus your actual requirements. Can you get away with the iterator not seeing all concurrent modifications?