Bruno Simões
Bruno Simões

Reputation: 721

How to avoid ConcurrentModificationException in multi-threaded code

Whenever we use java.util Collection classes, we have that if one thread changes a collection while another thread is traversing through it using an iterator, then any call to iterator.hasNext() or iterator.next() will throw ConcurrentModificationException. Even the synchronized collection wrapper classes SynchronizedMap and SynchronizedList are only conditionally thread-safe, which means all individual operations are thread-safe but compound operations where flow of control depends on the results of previous operations may be subject to threading issues. The question is: How to avoid this problem without affecting the performance. Note: I am aware of CopyOnWriteArrayList.

Upvotes: 8

Views: 3453

Answers (4)

Raedwald
Raedwald

Reputation: 48634

If you have an unencapsulated Collection object of a non thread-safe class, it is impossible to prevent misuse of the Collection, and thus the possibility of a ConcurrentModificationException.

Other answers have suggested use of a thread-safe Collection class, such as those provided by java.util.concurrent. You should however consider encapsulating the Collection object: have the object be private, and have your class provide higher level abstractions (such as addPerson and removePerson) that manipulate the Collection on behalf of the callers, and do not have any getter methods that return references to the Collection. It is then fairly easy to enforce invariants on the encapsulated data (such as "every person has a non empty name") and provide thread-safety using synchronized.

Upvotes: 0

Yanick Rochon
Yanick Rochon

Reputation: 53531

This is because the "standard" Java collections are not thread safe as they are not synchronized. When working with multiple threads accessing your collections, you should look at the java.util.concurrent packages.

Without this package, before Java 5, one had to perform a manual synchronization :

synchronized(list) {
   Iterator i = list.iterator(); // Must be in synchronized block
   while (i.hasNext())
       foo(i.next());
}

or using

Collections.synchronizedList(arrayList);

but neither could really offer a complete thread safety feature.

With this package, all access to the collection is made atomically and some classes provide a snapshot of the state of the list when the iterator was constructed (see CopyOnWriteArrayList. The CopyOnWriteArrayList is fast on read, but if you are performing many writes, this might affect performance.

Thus, if CopyOnWriteArrayList is not desired, take a look at ConcurrentLinkedQueue which offers a "weakly consistent" iterator that will never throw ConcurrentModificationException, and guarantees to traverse elements as they existed upon construction of the iterator. This one is efficient in all point, unless you have to access elements at specific indexes more often than traversing the entire collection.

Another option would be ConcurrentSkipListSet which provides expected average log(n) time cost for the contains, add, and remove operations and their variants. Insertion, removal, and access operations safely execute concurrently by multiple threads and iterators are weakly consistent as well.

Which concurrent (thread-safe) collections depend of what type of operations you perform the most with. And since they are all part of the Java Collection framework, you can swap them to which ever you need.

Upvotes: 1

Yair Zaslavsky
Yair Zaslavsky

Reputation: 4137

I think you raised an interesting question.
I tried thinking whether ConcurrentHashMap for example, as was suggested by others can help, but I'm not sure as the lock is segment-based.
What I would do in this case , and I do hope I understood your question well, is to lock access to your collection, using a ReaderWriterLock.
The reason I chose this lock is because I do feel this needs locking (as you explained - iteration is composed from several operations) ,
And because in case of reader threads, I do not want them to wait on lock , if no writer thread is working on the collection. Thanks to @Adam Arold I paid attention that you suggested the "synchronized decorator" - but I feel this decorator is "too strong" for your needs, as it uses a synchronized and will not diffrentiate between cases of N readers and M writers.

Upvotes: 1

Adam Arold
Adam Arold

Reputation: 30528

You can use CopyOnWriteArrayList or ConcurrentHashMap etc. as you mentioned above or you can use Atomic* classes which are working with CAS.

If you weren't aware of Atomic* classes they definitely worth a look! You may check out this question.

So to answer your question you have to choose the right tools for the task. Since you do not share the context with us I can just guess. In some situations CAS will perform better in others the concurrent Collections will.

If something isn't clear you can always check out the official Oracle Trails: Lesson: Concurrency

Upvotes: 2

Related Questions