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