Reputation: 1517
The Java Concurrency in Practice mentions that:
The iterator returned by the
ConcurrentHashMap
are weakly consistent than fail-fast. A weakly consistent iterator can tolerate the concurrent modifications, traverses elements as they existed when the iterator was constructed, and may (but is not guaranteed to) reflect modifications to the collection after the construction of the iterator.
ConcurrentHashMap
will be modified. The only thing is that it'll not throw the ConcurrentModificationException
.Upvotes: 2
Views: 1928
Reputation: 43117
First of all, the iterators of concurrent collections are not fail-safe because they do not have failure modes which they could somehow handle with some kind of emergency procedure. They simply do not fail.
The iterators of the non-concurrent collections are fail-fast because of performance reasons they are designed in a way that does not allow the internal structure of the collection they iterate over to be modified. E.g. a hashmap's iterator would not know how to continue iterating after the reshuffling that happens when a hashmap gets resized.
That means they would not just fail because other threads access them, they would also fail if the current thread performs a modification that invalidates the assumptions of the iterator.
Instead of ignoring those troublesome modifications and returning unpredictable and corrupted results those collections instead try to track modifications and throw an exception during iteration to inform the programmer that something is wrong. This is called fail-fast.
Those fail-fast mechanisms are not thread-safe either. Which means if the illegal modifications don't happen from the current thread but from a different threads they are not guaranteed to be detected anymore. In that case it can only be thought of as a best-effort failure detection mechanism.
On the other hand concurrent collections must be designed in a manner that can deal with multiple writes and reads at the same time and the underlying structure changing constantly.
So iterators can't always assume that the underlying structure is never modified during iteration.
Instead they're designed to provide weaker guarantees, such as either iterating over outdated data or maybe also showing some but not all updates that happened after the creation of the iterator. Which also means that they might return outdated data when they are modified during iteration within a single thread, which might be somewhat counter-intuitive for a programmer as one would usually expect immediate visibility of modifications within a single thread.
Examples:
HashMap
: best-effort fail-fast iterator.
clear()
ing the Map during iteration: guaranteed to throw a ConcurrentModificationException
on the next iterator stepCopyOnWriteArrayList
: snapshot iterator
clear()
ing the list will not stop iterationConcurrentSkipListMap
: weakly consistent iterator
clear()
ing the Map may or may not stop iteration and removing entries may or may not stop them from showing up during the remaining iterationUpvotes: 1
Reputation: 7620
Please keep in mind that Fail Fast iterator iterates over the original collection.
In contrast Fail Safe (a.k.a weakly consistent) iterator iterates over a copy of the original collection. Therefore any changes to the original collection go unnoticed, and that's how it guarantees lack of ConcurrentModificationException
s.
To answer your questions:
As you can see it's a trade-off between correctness of your use case and speed.
ConcurrentHashMap
(CHM) exploits multiple tricks in order to increase concurrency of access.
MapEntry
gets stored in one of the number of segments each itself being a hashtable which can be concurrently read (read
methods do not block).concurrencyLevel
(default 16). The number of segments determines the number of concurrent writers across the whole of the data. The equal spread of entries between the segments is ensured by additional internal hashing algorithm.HashMapEntry
s value is volatile
thereby ensuring fine grain consistency for contended modifications and subsequent reads; each read reflects the most recently completed updateUpvotes: 7
Reputation: 61168
If you want a consistent iterator, then you have to lock all modifications to the Map
- this is a massive penalty in a concurrent environment.
You can of course do this manually if that is what you want, but iterating over a Map
is not its purpose so the default behaviour allows for concurrent writes while iterating.
The same argument does not apply for normal collections, which are only (allowed to be) accessed by a single thread. Iteration over an ArrayList
is expected to be consistent, so the fail fast iterators enforce consistency.
Upvotes: 3