Java ConcurrentModificationException异常解决

来源:互联网 发布:java system类 编辑:程序博客网 时间:2024/05/22 22:40

上篇文章介绍Iterator遍历ArrayList时有可能引发ConcurrentModificationException异常产生的原因是modCount和expectedModCount的值不一致,具体介绍参见 Iterator迭代器

异常解决方法

1、单线程环境

仔细观察我们会发现Iterator也提供了一个remove()方法,实质也是调用了ArrayList中的remove,源码如下:

public void remove() {    if (lastRet == -1)    throw new IllegalStateException();        checkForComodification();    try {    AbstractList.this.remove(lastRet);    if (lastRet < cursor)        cursor--;    lastRet = -1;    expectedModCount = modCount;    } catch (IndexOutOfBoundsException e) {    throw new ConcurrentModificationException();    }}

从上面代码可以看出 增加了 expectedModCount = modCount; 所以不会抛出ConcurrentModificationException异常

2、多线程环境

在多线程环境下,我们再次验证上面代码

public class Test {    static List<String> list = new ArrayList<String>();    public static void main(String[] args) {        list.add("A");        list.add("B");        list.add("C");        list.add("D");        // 用于读取操作的线程        new Thread() {                public void run() {                    Iterator<String> ite = list.iterator();                    while (ite.hasNext()) {                        String str = ite.next();                        System.out.println(Thread.currentThread().getName() +                            ": " + str);                        try {                            Thread.sleep(100);                        } catch (InterruptedException e) {                            e.printStackTrace();                        }                    }                } ;            }.start();        // 用于删除操作的线程        new Thread() {                public void run() {                    Iterator<String> ite = list.iterator();                    while (ite.hasNext()) {                        String str = ite.next();                        if ("B".equals(str)) {                            ite.remove();                        }                    }                };            }.start();    }}

输出:

Thread-0: AException in thread "Thread-0" java.util.ConcurrentModificationException    at java.util.AbstractList$Itr.checkForComodification(AbstractList.java:372)    at java.util.AbstractList$Itr.next(AbstractList.java:343)    at com.collection.Test$1.run(Test.java:24)

出现异常的原因是一个线程修改了list的modCount,而导致另一个线程进行迭代时modCount与该迭代器的expectedModCount不相等

解决方法1、迭代前加synchronized锁

public class Test {    static List<String> list = new ArrayList<String>();    public static void main(String[] args) {        list.add("A");        list.add("B");        list.add("C");        list.add("D");        // 用于读取操作的线程        new Thread() {                public void run() {                    Iterator<String> ite = list.iterator();                    synchronized (list) { // 加锁                        while (ite.hasNext()) {                            String str = ite.next();                            System.out.println(Thread.currentThread().getName() +                                ": " + str);                            try {                                Thread.sleep(100);                            } catch (InterruptedException e) {                                e.printStackTrace();                            }                        }                    }                } ;            }.start();        // 用于删除操作的线程        new Thread() {                public void run() {                    Iterator<String> ite = list.iterator();                    synchronized (list) { // 加锁                        while (ite.hasNext()) {                            String str = ite.next();                            if ("B".equals(str)) {                                ite.remove();                            }                        }                    }                };            }.start();    }}

解决方法2、使用CopyOnWriteArrayList

public class Test {    static List<String> list = new CopyOnWriteArrayList<String>();    public static void main(String[] args) {        list.add("A");        list.add("B");        list.add("C");        list.add("D");        // 用于读取操作的线程        new Thread() {                public void run() {                    Iterator<String> ite = list.iterator();                    while (ite.hasNext()) {                        String str = ite.next();                        System.out.println(Thread.currentThread().getName() +                            ": " + str);                        try {                            Thread.sleep(100);                        } catch (InterruptedException e) {                            e.printStackTrace();                        }                    }                } ;            }.start();        // 用于删除操作的线程        new Thread() {                public void run() {                    Iterator<String> ite = list.iterator();                    while (ite.hasNext()) {                        String str = ite.next();                        if ("B".equals(str)) {                            list.remove(str);                        }                    }                } ;            }.start();    }}

CopyOnWrite容器即写时复制的容器。通俗的理解是当我们往一个容器添加元素的时候,不直接往当前容器添加,而是先将当前容器进行Copy,复制出一个新的容器,然后新的容器里添加元素,添加完元素之后,再将原容器的引用指向新的容器。这样做的好处是我们可以对CopyOnWrite容器进行并发的读,而不需要加锁,因为当前容器不会添加任何元素。所以CopyOnWrite容器也是一种读写分离的思想,读和写不同的容器。

原创粉丝点击