Interview question learning: fail-fast and fail-safe

Purpose

For the convenience of follow-up interview review, here is a personal study note record

source of learning materials

A learning video about high-frequency interview questions at Station B

Core knowledge points

Fail-fast: Concurrent modification is not supported. If concurrent modification occurs, ConcurrentModificationException will be thrown directly.
Fail-safe: Contrary to the above, concurrent modification is supported at the expense of consistency.
There is a check: checkForComodification()
ArrayList is a typical representative of Fail-fas, traverse It does not support modification at the same time, and fails as soon as possible.
CopyOnWritArrayList is a typical representative of Fail-safe, it can be modified while traversing, and the principle of separation of reading and writing.

source code excerpt

Part of the source code of ArrayList

ExpectedModCount is compared with modCount, if they are not equal, a concurrent modification exception (ConcurrentModificationException) is thrown directly

Partial source code of the iterator:

    /**
     * An optimized version of AbstractList.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();
        }

		// 逻辑很简单就是2个modCount做一个对比
        final void checkForComodification() {
    
    
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
        }
    }

CopyOnWriteArrayList part of the source code

Iterator part source code

Here are all operations on the array of snapshots: snapshot

    static final class COWIterator<E> implements ListIterator<E> {
    
    
        /** Snapshot of the array */
        private final Object[] snapshot;
        /** Index of element to be returned by subsequent call to next.  */
        private int cursor;

        private COWIterator(Object[] elements, int initialCursor) {
    
    
            cursor = initialCursor;
            snapshot = elements;
        }

        public boolean hasNext() {
    
    
            return cursor < snapshot.length;
        }

        @SuppressWarnings("unchecked")
        public E next() {
    
    
            if (! hasNext())
                throw new NoSuchElementException();
            return (E) snapshot[cursor++];
        }
}

The implementation principle of the add method

    public boolean add(E e) {
    
    
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
    
    
            Object[] elements = getArray();
            int len = elements.length;
            // 赋值原来的集合, 容量+1
            Object[] newElements = Arrays.copyOf(elements, len + 1);
            // 新元素放到新集合最后
            newElements[len] = e;
            setArray(newElements);
            return true;
        } finally {
    
    
            lock.unlock();
        }
    }

The idea of ​​reading and writing separation

CopyOnWriteArrayList actually maintains two arrays, read: actually read the previous array, because of the snapshot. Write: write to the new data. The original snapshot array will be automatically garbage collected if it is not referenced later.

Guess you like

Origin blog.csdn.net/xiaozhengN/article/details/127185514