fail-fast (fast failure) java in the mechanism

fail-fast (fast failure) java in the mechanism

Brief introduction

  fail-fast mechanism, i.e. rapid failure mechanism, an error detection mechanism is a set of java. When structural changes in the course of the iterative collection of the collection is that there are fail-fast it may occur that ran ConcurrentModificationException exception. fail-fast mechanism does not guarantee under certain modifications are not synchronized throws an exception, it's just best to throw the past, so this mechanism is generally used only to detect bug

 

fail-fast emergence scene

In our common java set to fail-fast mechanism that may arise, such as the common ArrayList, HashMap. Are likely to experience rapid failure in a multi-threaded and single-threaded environment .

1. fail-fast example of the single-threaded environment:
    public static void main(String[] args) {
           List<String> list = new ArrayList<>();
           for (int i = 0 ; i < 10 ; i++ ) {
                list.add(i + "");
           }
           Iterator<String> iterator = list.iterator();
           int i = 0 ;
           while(iterator.hasNext()) {
                if (i == 3) {
                     list.remove(3);
                }
                System.out.println(iterator.next());
                i ++;
           }
     }

Print Console:

Exception in thread "main" java.util.ConcurrentModificationException
    at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:901)
    at java.util.ArrayList$Itr.next(ArrayList.java:851)
    at com.example.springboot_demo.fail_fast_safe.ArrayListFailFast.main(ArrayListFailFast.java:24)

This code defines a collection Arraylist, and iterators to traverse, during traversal, deliberately remove an element in a certain iteration step, this time occurs fail-fast

2. Under multithreaded environment
public class ArrayListFailFastThreadPool {
    public static List<String> list = new ArrayList<>();
​
    private static class MyThread1 extends Thread {
        @Override
        public void run() {
            Iterator<String> iterator = list.iterator();
            while(iterator.hasNext()) {
                String s = iterator.next();
                System.out.println(this.getName() + ":" + s);
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            super.run();
        }
    }
​
    private static class MyThread2 extends Thread {
        int i = 0;
        @Override
        public void run() {
            while (i < 10) {
                System.out.println("thread2:" + i);
                if (i == 2) {
                    list.remove(i);
                }
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                i ++;
            }
        }
    }
​
    public static void main(String[] args) {
        for(int i = 0 ; i < 10;i++){
            list.add(i+"");
        }
        MyThread1 thread1 = new MyThread1();
        MyThread2 thread2 = new MyThread2();
        thread1.setName("thread1");
        thread2.setName("thread2");
        thread1.start();
        thread2.start();
    }
​
}

 

Print Console:

Exception in thread "thread1" java.util.ConcurrentModificationException
thread2:3
    at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:901)
    at java.util.ArrayList$Itr.next(ArrayList.java:851)
    at com.example.springboot_demo.fail_fast_safe.ArrayListFailFastThreadPool$MyThread1.run(ArrayListFailFastThreadPool.java:20)

 

Start two threads, one for each iteration of the list with the other in an iterative process threads 1 to remove an element, the result is thrown java.util.ConcurrentModificationException

Fail-fast principle

fail-fast ConcurrentModificationException is how to throw an exception, but also under what circumstances will throw?

We know, for a collection of the list, map type, we all can be traversed by the iterator, but Iterator is really just an interface specific implementation or specific internal class collection class that implements the Iterator and implement relevant methods. Here we have an example to the ArrayList class. In the ArrayList, when calling list.iterator (), its source code is:

 public Iterator<E> iterator() {
        return new Itr();
 }
 
 
  private class Itr implements Iterator<E> {
        int cursor;       // index of next element to return
        int lastRet = -1; // index of last element returned; -1 if no such
        int expectedModCount = modCount;
​
        public boolean hasNext() {
            return cursor != size;
        }
​
        @SuppressWarnings("unchecked")
        public E next() {
            checkForComodification();
            int i = cursor;
            if (i >= size)
                throw new NoSuchElementException();
            Object[] elementData = ArrayList.this.elementData;
            if (i >= elementData.length)
                throw new ConcurrentModificationException();
            cursor = i + 1;
            return (E) elementData[lastRet = i];
        }
​
        public void remove() {
            if (lastRet < 0)
                throw new IllegalStateException();
            checkForComodification();
​
            try {
                ArrayList.this.remove(lastRet);
                cursor = lastRet;
                lastRet = -1;
                expectedModCount = modCount;
            } catch (IndexOutOfBoundsException ex) {
                throw new ConcurrentModificationException();
            }
        }
​
        @Override
        @SuppressWarnings("unchecked")
        public void forEachRemaining(Consumer<? super E> consumer) {
            Objects.requireNonNull(consumer);
            final int size = ArrayList.this.size;
            int i = cursor;
            if (i >= size) {
                return;
            }
            final Object[] elementData = ArrayList.this.elementData;
            if (i >= elementData.length) {
                throw new ConcurrentModificationException();
            }
            while (i != size && modCount == expectedModCount) {
                consumer.accept((E) elementData[i++]);
            }
            // update once at end of iteration to reduce heap write traffic
            cursor = i;
            lastRet = i - 1;
            checkForComodification();
        }
        
        //这段代码是关键
        final void checkForComodification() {
            if (modCount != expectedModCount)
                throw newConcurrentModificationException (); 
        } 
    }
     As it can be seen, which is the key to determine whether the abnormal ConcurrentModificationException thrown. In this code segment, when modCount! = ExpectedModCount, the exception will be thrown. Obviously expectedModCount in addition to a start given initial value modCount outside, and did not recur throughout the iterative process of change, so change can occur only modCount. 
In front of the analysis of the ArrayList expansion mechanism, you can know be add in the ArrayList, remove, clear isochronous operations involving modification of the number of elements in the set, will change ModCount (modCount ++) so that when another thread (concurrent modification) or with a thread during the traversal, make method calls the relevant number of the sets is changed , will make modCount change, so checkForComodification ConcurrentModificationException method throws an exception.
Similarly, the principle of occurrence hashMap is the same.
Avoid fail-fast

method 1

In single-threaded during traversal, if you want to remove operation, you can call the iterator's remove method instead of collections remove method

 public static void main(String[] args) {
           List<String> list = new ArrayList<>();
           for (int i = 0 ; i < 10 ; i++ ) {
                list.add(i + "");
           }
           Iterator<String> iterator = list.iterator();
           int i = 0 ;
           while(iterator.hasNext()) {
                if (i == 3) {
                     iterator.remove(); //迭代器的remove()方法
                }
                System.out.println(iterator.next());
                i ++;
           }
     }

 

Method 2

  Using java and contracting (the java.util.concurrent) in place of class ArrayList and hashMap.

        CopyOnWriterArrayList instead of ArrayList, CopyOnWriterArrayList is almost the same with the use of the ArrayList, CopyOnWriter container is copy-on-write (COW), are thread-safe during read and write. The container is in the other of the add and remove operations performed on the original array is not modified, but one, to modify the original array in the new copy of the array, to be completed before the reference point to the old array to the new array, so for CopyOnWriterArrayList does not happen in an iterative process fail-fast phenomena. But CopyOnWrite container can only ensure that the final data consistency can not guarantee real-time data consistency.

        For HashMap, you can use ConcurrentHashMap, ConcurrentHashMap using the lock mechanism is thread-safe. In terms of iterations, ConcurrentHashMap use a different iterative manner. In this iterative manner, when then change the set after the iterator is created is no longer thrown ConcurrentModificationException, replaced by new data when new change so as not to affect the original data, and then complete the iterator will replace the head pointer the new data, so iterator thread can use the original old data, but can also be done to change the write thread concurrency. That will not happen iteration fail-fast, but not guaranteed to get the latest data.

 

Guess you like

Origin www.cnblogs.com/pingping-joe/p/11125002.html