it_worker365

   ::  ::  ::  ::  :: 管理

如何产生,一边遍历一边修改元素,产生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());
        }
    }
}

 

posted on 2017-05-16 14:33  it_worker365  阅读(624)  评论(0编辑  收藏  举报