ConcurrentModificationException原因及排除

本文探讨了在Java中遍历并修改集合时遇到的ConcurrentModificationException异常,通过示例展示了如何使用CopyOnWriteArrayList和迭代器的方法来避免该异常。

如何产生,一边遍历一边修改元素,产生iter后再修改原结构,如下,无论是for中或iter都会产生ConcurrentModificationException

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

/**
 * Created by itworker365 on 5/16/2017.
 */
public class ConcurrentModifyException {
    public static void main(String[] args) throws InterruptedException {
        List<String> a = new ArrayList<String>();
        a.add("a");
        a.add("b");
        a.add("c");
        final List<String> list = new ArrayList<String>(a);
        final Iterator<String> it = list.iterator();
        //cause ConcurrentModifyException,list.iterator已经生成iterator对象,此时再更改原结构报错
        a.add("d");
        Thread t = new Thread(new Runnable() {
            int count = -1;
            @Override
            public void run() {
                if (list.contains("a")) {
                    list.remove("a");
                }
            }
        });
        t.setDaemon(true);
        t.start();

        for (String s : list) {
            //cause ConcurrentModifyException,边读边改报错
            System.out.println(list.hashCode() + "   " + s);
        }
//        while (it.hasNext()) {
//            //cause ConcurrentModifyException,边读边改报错
//            System.out.println(it.next());
//        }
    }
}

产生原因:

        public E next() {
            checkForComodification();
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();

解决办法:

1. 用CopyOnWriteArrayList, 可以发现在打印hashcode的时候并不相同,这种结构在修改元素时使用Arrays.CopyOf复制元素,然后在新List中修改之后修改引用

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;

/**
 * Created by itworker365 on 5/16/2017.
 */
public class ConcurrentModifyException {
    public static void main(String[] args) throws InterruptedException {
        List<String> a = new ArrayList<String>();
        a.add("a");
        a.add("b");
        a.add("c");
        final List<String> list = new CopyOnWriteArrayList<String>(a);
        final Iterator<String> it = list.iterator();
        Thread t = new Thread(new Runnable() {
            int count = -1;
            @Override
            public void run() {
                if (list.contains("a")) {
                    list.remove("a");
                }
            }
        });
        t.setDaemon(true);
        t.start();
        for (String s : list) {
            System.out.println(list.hashCode() + "   " + s);
        }
        while (it.hasNext()) {
            System.out.println(it.next());
        }
    }
}

CopyOnWriteArrayList关键实现,一看既懂,需要注意的是内存使用,可能复制后会出发gc,因为写操作完成并且引用指向新数组之前,其他并发读取线程只能看到旧数据,所以只是最终一致性,不能实时。

private volatile transient Object[] array;
    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();
        }
    }
    final void setArray(Object[] a) {
        array = a;
    }

2. 使用Iterator去除,迭代器支持的功能有限

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;

/**
 * Created by itworker365 on 5/16/2017.
 */
public class ConcurrentModifyException {
    public static void main(String[] args) throws InterruptedException {
        List<String> a = new ArrayList<String>();
        a.add("a");
        a.add("b");
        a.add("c");
        final List<String> list = new ArrayList<String>(a);
        final Iterator<String> it = list.iterator();
        Thread t = new Thread(new Runnable() {
            @Override
            public void run() {
                while (it.hasNext()) {
                    String xx = it.next();
                    it.remove();
                }
            }
        });
        t.setDaemon(true);
        t.start();
        while (it.hasNext()) {
            System.out.println(it.next());
        }
    }
}

 

转载于:https://www.cnblogs.com/it-worker365/p/6861237.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值