A system is called fail-fast if it stops immediately its execution when an error occurred. The system does not continue with the errors and these errors in the fail fast systems are immediately exposed. But, fail-safe system does the opposite thing. The fail-safe system does not stop its execution despite an error or a fault is occurred in the system. The system continues with the operations hiding the errors instead of exposing the errors immediately.
The only difference is fail safe iterator doesn’t throw any Exception but fail-fast Iterator. A collection is modified structurally while one thread is iterating over it, because the modification happens on clone of a collection instead of original collection and that’s why they are called as fail-safe iterators.
From one of the javadoc in HashMap
The fail-fast behavior of an iterator cannot be guaranteed as it is, generally speaking, impossible to make any hard guarantees in the presence of unsynchronized concurrent modification. Fail-fast iterators throw ConcurrentModificationException on a best-effort basis. Therefore, it would be wrong to write a program that depended on this exception for its correctness: the fail-fast behavior of iterators should be used only to detect bugs.
Fail Fast and fail Safe Iterators
Iterators in Java allow you to iterate through the Collection objects for traversing the elements in the Collection objects. Iterators returned by the Collection are either fail-fast or fail-safe in nature.
The only difference is fail-safe iterator doesn’t throw any exception, contrary to fail-fast Iterator, if a Collection is modified structurally while one thread is iterating over it. This is because the Iterator works on clone of Collection instead of original collection and that’s why it is called as fail-safe iterator. That’s why fail-safe iterators require extra memory to clone the collection, whereas fail-fast iterators don’t require extra memory.
Iterator of CopyOnWriteArrayList
is an example of fail-safe Iterator and also iterator written by ConcurrentHashMap
keySet is also fail-safe iterator and never throw ConcurrentModificationException
in Java. But Iterators of ArrayList
, HashMap
etc. are fail-fast iterators.
Fail Fast Example
The following Java code shows how fail-fast iterator works.
package com.roytuts.java.fail.fast.fail.safe;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.TreeMap;
public class FailFastExample {
public static void main(String[] args) {
Map<String, String> llMap = new TreeMap<>();
llMap.put("RedHat", "Unix");
llMap.put("Google", "Android");
llMap.put("Apple", "iOS");
llMap.put("Microsoft", "Windows");
// iterate over HashMap
llMap.forEach((k, v) -> System.out.println(k + " => " + v));
System.out.println();
// iterate using iterator
Iterator<Entry<String, String>> iterator = llMap.entrySet().iterator();
while (iterator.hasNext()) {
Map.Entry<java.lang.String, java.lang.String> entry = (Map.Entry<java.lang.String, java.lang.String>) iterator
.next();
System.out.println(entry.getKey() + " => " + entry.getValue());
}
Set<String> keys = llMap.keySet();
for (Object key : keys) {
llMap.remove(key); // it will throw the ConcurrentModificationException here
llMap.put("abc", "value"); // it will throw the
// ConcurrentModificationException here
}
}
}
Output
Running the above program will give you the following example:
Apple => iOS
Google => Android
Microsoft => Windows
RedHat => Unix
Apple => iOS
Google => Android
Microsoft => Windows
RedHat => Unix
Exception in thread "main" java.util.ConcurrentModificationException
at java.base/java.util.TreeMap$PrivateEntryIterator.nextEntry(TreeMap.java:1208)
at java.base/java.util.TreeMap$KeyIterator.next(TreeMap.java:1262)
at com.roytuts.java.fail.fast.fail.safe.FailFastExample.main(FailFastExample.java:34)
Fail Safe Example
The following Java code shows an example on fail safe iterator.
package com.roytuts.java.fail.fast.fail.safe;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
public class FailSafeExample {
public static void main(String[] args) {
ConcurrentMap<String, String> cMap = new ConcurrentHashMap<>();
cMap.put("RedHat", "Unix");
cMap.put("Google", "Android");
cMap.put("Apple", "iOS");
cMap.put("Microsoft", "Windows");
cMap.putIfAbsent("RedHat", "Unix");
cMap.forEach((k, v) -> System.out.println(k + " => " + v));
Set<String> keys = cMap.keySet();
for (Object key : keys) {
// cMap.remove(key); //will not throw ConcurrentModificationException here
cMap.put("abc", "value"); // will not throw ConcurrentModificationException here
}
System.out.println();
cMap.forEach((k, v) -> System.out.println(k + " => " + v));
}
}
Output
Running the above program will give you the following output:
RedHat => Unix
Google => Android
Apple => iOS
Microsoft => Windows
RedHat => Unix
Google => Android
Apple => iOS
abc => value
Microsoft => Windows