• ArrayList 并发操作 ConcurrentModificationException 异常


    1、故障现象

    ArrayList在迭代的时候如果同时对其进行修改就会抛出java.util.ConcurrentModificationException异常

    2、故障代码

    public class ArrayListTest {
    
        public static void main(String[] args) {
    
            List<String> lists = new ArrayList<>();
            lists.add("a");
            lists.add("b");
    
            Iterator<String> iterator = lists.iterator();
            while (iterator.hasNext()) {
                String next = iterator.next();
                if (next == "b") {
                    lists.remove(next);
                }
            }
        }
    }
    

    异常截图

    3、导致原因

    通过查看异常,发现异常出现的位置在 java.util.ArrayList类的内部类Itr中的checkForComodification方法中

    /**
     * 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;
    
    	Itr() {}
    
    	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 new ConcurrentModificationException();
    	}
    }
    

    通过查看代码发现如果 modCount和expectedModCount不相等就会导致抛出异常。

    modCount是修改记录数,expectedModCount是期望修改记录数,初始化时expectedModCount=modCount。

    ArrayList集合的add和remove操作都有对modCount++操作,就会导致expectedModCount和modCount值不相等从而产生ConcurrentModificationException异常

    4、解决方案

    解决方案1:使用iterator的remove操作替代ArrayList集合自己的remove操作

    public class ArrayListTest {
    
        public static void main(String[] args) {
            List<String> lists = new ArrayList<>();
            lists.add("a");
            lists.add("b");
    
            Iterator<String> iterator = lists.iterator();
            while (iterator.hasNext()) {
                String next = iterator.next();
                if (next == "b") {
                    iterator.remove();
                }
            }
        }
    }
    

    分析:通过查看iterator的remove方法发现,其实还是调用了ArrayList集合的remove方法移除元素,但是会使

    expectedModCount=modeCount所以不会抛出ConcurrentModificationException异常

    解决方案2:使用JUC concurrent包中CopyOnWriteArrayList并发集合类

    public class ArrayListTest {
    
        public static void main(String[] args) {
            List<String> lists = new CopyOnWriteArrayList<>();
            lists.add("a");
            lists.add("b");
    
            Iterator<String> iterator = lists.iterator();
            while (iterator.hasNext()) {
                String next = iterator.next();
                if (next == "b") {
                    lists.remove(next);
                    System.out.println(lists);
                }
            }
        }
    }
    

    因为迭代器中没有checkForComodification操作,并且集合的add和remove方法中都通过ReentrantLock加锁保证并发操作下的安全性。

    5、优化建议

    在对ArrayList集合进行并发操作时尽量使用CopyOnWriteArrayList集合类代替

  • 相关阅读:
    自动部署基于Maven的war文件到远程Tomcat
    解决Eclipse中新创建的Maven项目不自动创建web.xml文件
    JFreeChart
    hibernate
    hibernate
    hibernate
    hibernate
    Hibernate
    hibernate关联关系映射详解
    Hibernate获取数据java.lang.StackOverflowError
  • 原文地址:https://www.cnblogs.com/dtdx/p/12313629.html
Copyright © 2020-2023  润新知