SoFunction
Updated on 2025-03-04

Detailed explanation of the use of fail-fast mechanism in Java

Java's fail-fast mechanism is used

The fail-fast mechanism is an error mechanism in Java collections (Collection). When multiple threads operate on the contents of the same collection, a fail-fast event may be generated.

For example:

  • When a thread A traverses a collection through iterator, if the content of the collection is changed by other threads, when thread A accesses the collection, it will throw a ConcurrentModificationException exception, generating a fail-fast event.
  • The operations here mainly refer to add, remove and clear, modifying the number of set elements.

Example code

Single threading, when removing/adding elements of certain collection elements in the foreach loop, the fail-fast mechanism will be triggered.

public static void main(String[] args){
  List<String> strList = new ArrayList<>();
  ("AA");
  ("aa");
  ("BB");
  ("CC");
  for(String str : strList){
     if("aa".equals(str)){
       (str);
     }
  }
}

Multi-threading, when one thread reads, another thread writes to the list, and the reading thread will fail-fast

// testpublic class TreadDemo1 {
   public static void main(String[] args){
        List&lt;String&gt; strList = new ArrayList&lt;&gt;();
        ("AA");
        ("aa");
        ("BB");
        ("CC");
        ("DD");
        new MyThread1(strList).start();
        new MyThread2(strList).start();
   }
   static class Mythread1 extends Thread {
       private List&lt;String&gt; list;
       public Mythread1(List&lt;String&gt; list){
              = list;
       }
       @Override
       public void run(){
             for(String str : list){
                 try{
                     (100);
                 }catch(InterruptedException e){
                     ();
                 }
                 ("MtThread1:"+str);
             }
       }
  }
  static class MyThread2 extends Thread {
       private list&lt;String&gt; list;
       public Mythread2(List&lt;String&gt; list){
              = list;
       }
       @Override
       public void run(){
             for(int i = 0; i &lt; (); i++){
                 try{
                     (100);
                 }catch(InterruptedException e){
                     ();
                 }
                 if("aa".equals((i))){
                     (i);
                 }
             }
             ("MtThread2:"+list);
       }
  }
}

principle

After decompiling the single-threaded .class, it was found that foreach actually relies on while loops and Iterator to implement it.

By tracking the exception stack of the code, the code that actually throws the exception is: $(); This method is called in the() method:

final void checkForComodification(){
      if(modCount != expectedModCount)
           throw new ConcurrentModificationException();
}

In this method, modCount and expectedModCount are compared. If the two are not equal, a ConcurrentModificationException exception is thrown.

  • modCountis a member variable in ArrayList. Indicates the number of times the set is actually modified. (Operating the remove(), add(), and clear() methods of the collection class will change the value of this variable)
  • expectedModCountIt is a member variable in an internal class in ArrayList - Itr(Iterator interface). Indicates the number of times the iterator expects the set to be modified. Its value is initialized as Itr is created. This value will only change if the set is operated through an iterator.

Therefore, when using Java collection classes, if a ConcurrentModificationException exception occurs, the fail-fast-related situation is given priority.

Solution

1) Use a normal for loop to operate

The normal for loop does not use the Iterator traversal, so the fail-fast test will not be performed.

public static void main(String[] args){
        List<String> strList = new ArrayList<>();
        ("AA");
        ("aa");
        ("BB");
        ("CC");
        ("DD");
        for(int i = 0; i < (); i++){
                 if("aa".equals((i))){
                     (i);
                 }
        }
   }

2) Directly use Iterator to operate

public static void main(String[] args){
        List<String> strList = new ArrayList<>();
        ("AA");
        ("aa");
        ("BB");
        ("CC");
        ("DD");
        Iterator<String> iterator = ();
        while(()){
             if("aa".equals(())){
                ();
             }
        }
   }

3) Use filters provided in Java 8

In Java 8, the collection can be converted into a stream. There is a filter operation for the stream, which can filter the original Stream, and the filtered elements are left to generate a new Stream.

public static void main(String[] args){
        List<String> strList = new ArrayList<>();
        ("AA");
        ("aa");
        ("BB");
        ("CC");
        ("DD");
        strList = ().filter(e -> !"aa".equals(e)).collect(());
        (strList);
   }

4) Collection class using fail-safe

In order to avoid triggering the fail-fast mechanism to cause exceptions, we can use some collection classes provided in Java that use the fail-safe mechanism.

The containers under the package are all fail-safe and can be used concurrently and modified in multiple threads. At the same time, you can also perform add/remove operations in foreach.

5) You can also use foreach loops

If we are very sure that an element to be deleted in a collection contains only one, we can also use the foreach loop. As long as we delete it, the loop body will be immediately terminated and the traversal will not be continued.

public static void main(String[] args){
  List<String> strList = new ArrayList<>();
  ("AA");
  ("aa");
  ("BB");
  ("CC");
  for(String str : strList){
     if("aa".equals(str)){
       (str);
       break;
     }
  }
  (strList);
}

Summarize

The above is personal experience. I hope you can give you a reference and I hope you can support me more.