The standard way to iterate a Collection is with an Iterator, either explicitly or through the for-each loop. If the collection has changed since iteration began, the iterator will throw the unchecked ConcurrentModificationException.
Guarding the iterator with a correct lock is easy, but the challenge is to find all the hidden iterators that need to be guarded.
In the following example, the System.out.println has a hidden iterator -- string concatenation gets turned by the compiler into a call to StringBuilder.append(Object), which in turn invokes the HashSet's toString method, which iterates the collection and calls toString on each element. As a result, while main thread is printing, the other thread could be removing elements, so the main thread sometimes throws ConcurrentModificationException.
import java.util.HashSet;
import java.util.Set;
public class HiddenIterator {
private final Set<Integer> set = new HashSet<Integer>();
public synchronized void add(Integer i) {
set.add(i);
}
public synchronized void remove(Integer i) {
set.remove(i);
}
public void addTenThings() {
for (int i = 0; i < 10; i++)
add(i);
System.out.println("DEBUG: added ten elements to " + set); //println called set.toString(), which used the iterator
//new HashSet<Integer>().toString();
}
public static void main(String[] args) {
HiddenIterator h = new HiddenIterator();
h.addTenThings();
new Thread(new Runnable(){
@Override
public void run() {
for(int i = 0; i < 5; i++) {
h.remove(i);
}
}}).start();
h.addTenThings();
}
}