Reputation: 721
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
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
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
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
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