public synchronized X getAnotherX(){
if(iterator.hasNext()){
X b = iterator.next();
String name = b.getInputFileName();
...
return b;
}
else{return null;}
}
despite the synchronized statement in the declaration header, i still get a ConcurrentModificationException Exception at the line where i use iterator.next(); whats wrong here ?
A retrieval operation will return the value inserted by the most recent completed insert operation. A lock is required for read operation too in SynchronizedHashMap. ConcurrentHashMap doesn't throw a ConcurrentModificationException if one thread tries to modify it while another is iterating over it.
How do you fix Java's ConcurrentModificationException? There are two basic approaches: Do not make any changes to a collection while an Iterator loops through it. If you can't stop the underlying collection from being modified during iteration, create a clone of the target data structure and iterate through the clone.
ConcurrentModificationException
usually has nothing to do with multiple threads. Most of the time it occurs because you are modifying the collection over which it is iterating within the body of the iteration loop. For example, this will cause it:
Iterator iterator = collection.iterator();
while (iterator.hasNext()) {
Item item = (Item) iterator.next();
if (item.satisfiesCondition()) {
collection.remove(item);
}
}
In this case you must use the iterator.remove()
method instead. This occurs equally if you are adding to the collection, in which case there is no general solution. However, the subtype ListIterator
can be used if dealing with a list and this has an add()
method.
I agree with the statements above about ConcurrentModificationException
often happening as a result of modifying the collection in the same thread as iterating. However, it is not always the reason.
The thing to remember about synchronized
is that it only guarantees exclusive access if everybody accessing the shared resource also synchronizes.
For example, you can synchronize access to a shared variable:
synchronized (foo) {
foo.setBar();
}
And you can think that you have exclusive access to it. However, there is nothing to stop another thread just doing something without the synchronized
block:
foo.setBar(); // No synchronization first.
Through bad luck (or Murphy's Law, "Anything that can go wrong, will go wrong."), these two threads can happen to execute at the same time. In the case of structural modifications of some widely-used collections (e.g. ArrayList
, HashSet
, HashMap
etc), this can result in a ConcurrentModificationException
.
It is hard to prevent the problem entirely:
You can document synchronization requirements, e.g. inserting "you must synchronize on blah
before modifying this collection" or "acquire bloo
lock first", but that's relying upon users to discover, read, understand and apply the instruction.
There is the javax.annotation.concurrent.GuardedBy
annotation, which can help to document this in a standardized way; the problem is then that you have to have some means of checking correct use of the annotation in the toolchain. For example, you might be able to use something like Google's errorprone, which can check in some situations, but it's not perfect.
For simple operations on collections, you can make use of the Collections.synchronizedXXX
factory methods, which wrap a collection so that every method call synchronizes on the underlying collection first, e.g. the SynchronizedCollection.add
method:
@Override public boolean add(E e) {
synchronized (mutex) { return c.add(obj); }
}
Where mutex
is the synchronized-on instance (often the SynchronizedCollection
itself), and c
is the wrapped collection.
The two caveats with this approach are:
You have to be careful that the wrapped collection cannot be accessed in any other way, since that would allow non-synchronized access, the original problem. This is typically achieved by wrapping the collection immediately on construction:
Collections.synchronizedList(new ArrayList<T>());
The synchronization is applied per method call, so if you are doing some compound operation, e.g.
if (c.size() > 5) { c.add(new Frob()); }
then you don't have exclusive access throughout that operation, only for the size()
and add(...)
calls individually.
In order to get mutually exclusive access for the duration of the compound operation, you would need to externally synchronize, e.g. synchronized (c) { ... }
. This requires you to know the correct thing to synchronize on, however, which may or may not be c
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With