有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

java CopyOnWriteArrayList/ConcurrentHashMap如何在内部处理并发修改异常?

我想从内部了解在像ConcurrentHashMapCopyOnWriteArrayList这样的并发集合中如何处理并发修改异常

互联网上有很多博客,建议使用这两种数据结构来避免并发修改异常。但并没有解释并发集合如何在内部处理此异常

有人能对此给出更多的见解吗?我需要一些详细的解释


共 (3) 个答案

  1. # 1 楼答案

    现在,这将回答CopyOnWriteArrayList如何避免ConcurrentModificationException的需要

    修改集合时,CopyOnWriteArrayList会执行两项操作

    1. 它防止其他线程通过锁定修改集合
    2. 将当前CopyOnWriteArrayList中的所有元素复制到新数组中,然后将该新数组分配给类的数组实例

    那么,这是如何预防CME的呢?标准集合中的CME将仅作为迭代的结果抛出。如果在对集合进行迭代时,在同一集合实例上执行添加或删除操作,则会引发异常

    CopyOnWriteArrayList的迭代器将当前数组指定为集合的最终字段快照,并将其用于迭代。如果另一个线程(甚至是同一个线程)尝试添加到CopyOnWriteArrayList,则更新将应用于新副本,而不是我们当前迭代的快照

    例如,我们知道add方法如下所示

    public boolean add(E e) {
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            Object[] elements = getArray();
            int len = elements.length;
            Object[] newElements = Arrays.copyOf(elements, len + 1);
            newElements[len] = e;
            setArray(newElements);
            return true;
        } finally {
            lock.unlock();
        }
    }
    

    请注意正在进行的线程本地新元素分配,完成后将设置为类实例volatile数组

    然后是迭代器,它被定义为

    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;
    

    因此,在迭代时,我们正在读取任何修改之前的数组,因为没有其他线程可以修改快照,所以我们正在查看ConcurrentModificationException不会发生

  2. # 2 楼答案

    你问题的字面答案不是很有趣ConcurrentHashMapCopyOnWriteArrayList不抛出ConcurrentModificationException,因为它们不包含抛出它的代码

    它不像ConcurrentModificationException是某种低级的内在事物ArrayListHashMap以及其他集合类,抛出ConcurrentModificationException以帮助您。它们必须包含额外代码,以尝试检测并发修改,并包含引发异常的额外代码^当其中一个类检测到某个地方存在导致对集合进行不安全修改的bug时,{}被抛出

    支持安全并发修改的类不会抛出ConcurrentModificationException,因为它们不需要抛出

    如果您正在尝试调试ConcurrentModificationException,那么还有很多其他问题可以帮助您回答:

  3. # 3 楼答案

    下面是ArrayListCopyOnWriteArrayListadd()方法定义

    阵列列表:

    public boolean add(E e) {
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        elementData[size++] = e;
        return true;
    }
    

    CopyOnWriteArrayList:

    public boolean add(E e) {
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            Object[] elements = getArray();
            int len = elements.length;
            Object[] newElements = Arrays.copyOf(elements, len + 1);
            newElements[len] = e;
            setArray(newElements);
            return true;
        } finally {
            lock.unlock();
        }
    }
    

    从上面的代码中可以清楚地看出CopyOnWriteArrayList在修改映射之前会锁定。这里我刚刚发布了add方法的代码。如果查看remove()/addAll()或任何method which modifiesList{}代码,可以看到它在修改集合之前已锁定。另外,ArrayList的迭代器方法(如next()/remove())检查修改,但CopyOnWriteArrayList的迭代器方法不检查修改。例如:

    ArrayList迭代器next()方法:

        @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];
        }
    

    CopyOnWriteArrayList迭代器next()方法:

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