Fail-Safe and Fail-Fast are two concepts in Java that describe how an iteration over a collection behaves when the collection is modified during the iteration. They are primarily related to how collections handle concurrent modifications during iteration, which can either fail immediately or continue working safely.
1. Fail-Fast Iterators:
Fail-Fast iterators immediately throw a ConcurrentModificationException
if the collection is structurally modified after the iterator is created, except through the iterator’s own methods (remove
in particular). These iterators detect changes in the collection by using a modification count (modCount) during iteration.
Characteristics of Fail-Fast Iterators:
- Immediate Failure: When the collection is modified (such as adding or removing elements), it will fail by throwing a
ConcurrentModificationException
at the first sign of modification. - Unsafe for Concurrent Modifications: Fail-Fast iterators are not thread-safe. They do not allow the collection to be modified during iteration, either in single-threaded or multi-threaded environments, unless done using the iterator’s
remove()
method. - Used by: Most collection classes in the
java.util
package, such asArrayList
,HashSet
,HashMap
, etc., provide fail-fast iterators.
Example of Fail-Fast:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
import java.util.ArrayList; import java.util.Iterator; import java.util.List; public class FailFastExample { public static void main(String[] args) { List<String> list = new ArrayList<>(); list.add("A"); list.add("B"); list.add("C"); Iterator<String> iterator = list.iterator(); while (iterator.hasNext()) { System.out.println(iterator.next()); // Modifying the list while iterating list.add("D"); // This will cause a ConcurrentModificationException } } } |
Output:
1 2 |
A Exception in thread "main" java.util.ConcurrentModificationException |
How Fail-Fast Works:
- Collections like
ArrayList
maintain an internalmodCount
. Each structural modification (likeadd
orremove
) increases this count. - The iterator checks the
modCount
on eachnext()
orhasNext()
call. If themodCount
has changed since the iterator was created, it throws aConcurrentModificationException
.
2. Fail-Safe Iterators:
Fail-Safe iterators, on the other hand, do not throw exceptions if the collection is modified during iteration. Instead, they work on a copy of the collection (usually a shallow copy), so they do not see modifications made to the original collection. The original collection can be modified without affecting the iteration.
Characteristics of Fail-Safe Iterators:
- Safe for Concurrent Modifications: They allow modifications to the collection while iterating without throwing exceptions, but the changes may not be reflected in the iteration.
- No ConcurrentModificationException: Fail-Safe iterators are designed to avoid
ConcurrentModificationException
by iterating over a separate copy of the collection. - Used by: Fail-Safe iterators are mostly used by classes in the
java.util.concurrent
package, such asConcurrentHashMap
,CopyOnWriteArrayList
, etc.
Example of Fail-Safe:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
import java.util.Iterator; import java.util.concurrent.ConcurrentHashMap; public class FailSafeExample { public static void main(String[] args) { ConcurrentHashMap<String, String> map = new ConcurrentHashMap<>(); map.put("1", "A"); map.put("2", "B"); Iterator<String> iterator = map.keySet().iterator(); while (iterator.hasNext()) { System.out.println(iterator.next()); // Modifying the map while iterating map.put("3", "C"); // No exception thrown } System.out.println("Map after iteration: " + map); } } |
Output:
1 2 3 |
1 2 Map after iteration: {1=A, 2=B, 3=C} |
How Fail-Safe Works:
- Fail-Safe iterators internally work on a clone or snapshot of the collection. For example,
ConcurrentHashMap
does not iterate over the original structure but over an internal snapshot of the keys or entries at the time the iteration was started. - This makes it safe from modifications but may result in iterating over stale data (i.e., the iterator does not reflect modifications made after it was created).
Differences Between Fail-Fast and Fail-Safe:
Feature | Fail-Fast | Fail-Safe |
---|---|---|
Modification Detection | Detects concurrent modifications and throws ConcurrentModificationException . |
Does not throw an exception on concurrent modification. |
Thread-Safety | Not thread-safe, even in single-threaded environments it fails on modification during iteration. | Thread-safe, allows concurrent modifications. |
Underlying Collection | Iterates over the original collection. | Iterates over a copy or snapshot of the collection. |
Used By | Non-concurrent collections like ArrayList , HashMap , etc. |
Concurrent collections like ConcurrentHashMap , CopyOnWriteArrayList , etc. |
Conclusion:
- Fail-Fast is useful in detecting errors during modification of collections while iterating.
- Fail-Safe is used in scenarios where you need thread safety and expect the collection to be modified concurrently, but you don’t care about reflecting those changes during the iteration process.