Java's fail fast mechanism
First, let’s talk about the phenomenon. When we use the iterator iterator to iterate through a collection, if other threads add or delete a key-value to the collection itself, the current thread will throw a ConcurrentModificationException exception.
Then talk about the principle. Whether it is AbstractList or HashMap, they all have a modCount variable. Whenever we add or delete elements to the set, the value of modCount will be increased by 1. This parameter is like the operation version number of the set. Before iteration, we will create an expectedModCount, which records the operation version number before iteration. Each time we get data through iteration, we will check whether the current modCount is as good as expectedModCount. If it is not equal, it means that other threads operate the collection when it is iterating over the elements.
The collections below the collection package are not thread-safe and will have concurrency problems, so they all contain modCount variables. Using the fail fast mechanism, when one thread is iterating and another thread adds or deletes elements, an error will be reported immediately.
Note: One thread is iterating over and the other thread does not trigger fail fast when modifying elements. You can look at the set() source code of ArrayList and LinkedList, and no traces of modCount are found (as long as you don't delete or add new elements, there is no problem).
Moreover, fail fast is just an error detection mechanism, because JDK cannot guarantee that fail fast will happen. If you use a collection of fail-fast mechanism in a multi-threaded environment, it is recommended to use "classes under package" instead of "classes under package".
It’s no point to say it, let’s take a look at the source code. Take LinkedList as an example, the code is as follows:
LinkedList<String> list = new LinkedList<>(); ("123"); ("456"); ("789"); for (String str : list) { (); }
There will be no error when executing() for the first time, but when entering the next round of loop, an error will be reported.
Before we use a for loop to traverse any List, the underlying layer will call the listIterator( ) method to create a ListIterator object for traversal. LinkedList is no exception. It overrides the listIterator() method and returns a custom ListItr object.
public ListIterator<E> listIterator(int index) { checkPositionIndex(index); return new ListItr(index); } private class ListItr implements ListIterator<E> { // Omitted.. private int expectedModCount = modCount; ListItr(int index) { Omitted.. } public E next() { checkForComodification(); Omitted.. } final void checkForComodification() { if (modCount != expectedModCount) throw new ConcurrentModificationException(); } }
I have omitted a lot of code that is not related to this article, and there are only a few key codes. When executing the listIerator() method, a ListItr object will be created, and its member variable expectedModCount will be initialized, which is equal to the current modCount.
The next step is traversal. The traversal is nothing more than getting the next element in the List. Think about it with your butt. Isn't the next() method called? The next() method executes checkForComodification() as soon as it comes up, and compare whether the current modCount is the same as expectedModCount. If it is different, an exception will be thrown.
Isn't this mean that if you dare to add or delete operations (absorb modCount's value) during the process of traversing the List, you will report an error.
It is worth noting that the error is not (). The error is reported when you make the next round of traversal after you do the new and delete operations, an error will be reported when you get the data.
Will there be an error when traversing the collection, adding or deleting new (or other operations that change modCount) will be reported?
No, as long as you bypass the method containing modCount check when querying data, you will not report an error.
String str; while((str = ()) != null) { (str); (); }
The above code is also traversing the linked list, but there will be no errors, because () does not modify the value of modCount, and peekFirst() does not check the modCount. Fail fast no longer exists, so there will be no errors.
This is the end of this article about the detailed explanation of the fail fast mechanism in the Java collection package. For more information about the fail fast mechanism of Java, please search for my previous articles or continue browsing the related articles below. I hope everyone will support me in the future!