Java-depth study 07: ConcurrentModificationException abnormal and CopyOnWriteArrayList
First, look at a single thread read Iterator traversal abnormal
1- code is as follows: Create a ArrayList, and add three elements; open a thread, traverses the ArrayList, while reading the data, while deleting data
public class CopyOnWriteArrayListTest { public static void main(String[] args) { CopyOnWriteArrayListThread t = new CopyOnWriteArrayListThread(); for(int i = 0; i< 1; i++){ new Thread(t).start(); } } } class CopyOnWriteArrayListThread implements Runnable{ //方式1 public static List<String> list = new ArrayList<String>(); static{ list.add("AA"); list.add("BB"); list.add("CC"); } @Override public void run() { Iterator<String> iterator = list.iterator(); while(iterator.hasNext()){ String str = iterator.next(); System.out.println(str); if(str.equals("CC")){ list.remove(str); } } } }
2- The results are as follows: Exception Report ConcurrentModificationException
java.util.ConcurrentModificationException at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:901) at java.util.ArrayList$Itr.next(ArrayList.java:851) at juc.concurrentmap.CopyOnWriteArrayListThread.run(CopyOnWriteArrayListTest.java:42) at java.lang.Thread.run(Thread.java:748)
3- Causes ConcurrentModificationException abnormality;
When performing iterator.next () method, we will carry out internal checkForComodification () judgment, when they throw ConcurrentModificationException modCount = expectedModCount when abnormal!;
Wherein when the number of times ModCount ArrayList modified record, modify expectations expectedModCount ArrayList number is, its initial value ModCount;
list.remove within (str) operation performed modCount ++; however expectedModCount and the amount of rice is updated, so the error;
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]; } final void checkForComodification() { if (modCount != expectedModCount) throw new ConcurrentModificationException(); }
4 the single-threaded solution: change list.remove (str) is iterator.remove (); ArrayList Iterator remove method in which the updated value expectedModCount
@Override public void run() { Iterator<String> iterator = list.iterator(); while(iterator.hasNext()){ String str = iterator.next(); System.out.println(str); if(str.equals("CC")){ iterator.remove(); } } }
private class Itr implements Iterator<E> { //ArrayList中Iterator的remove方法 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(); } } }
Second, multithreaded, even if iterator.remove (); still reported ConcurrentModificationException;
1- code is as follows: Create a ArrayList, and add three elements; open 10 threads, traverse the ArrayList, while reading the data, while deleting data
public class CopyOnWriteArrayListTest { public static void main(String[] args) { CopyOnWriteArrayListThread t = new CopyOnWriteArrayListThread(); for(int i = 0; i< 10; i++){ new Thread(t).start(); } } } class CopyOnWriteArrayListThread implements Runnable{ //方式1 public static List<String> list = new ArrayList<String>(); static{ list.add("AA"); list.add("BB"); list.add("CC"); } @Override public void run() { Iterator<String> iterator = list.iterator(); while(iterator.hasNext()){ String str = iterator.next(); System.out.println(str); if(str.equals("CC")){ iterator.remove(); } } } }
Exception log
java.util.ConcurrentModificationException at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:901) at java.util.ArrayList$Itr.next(ArrayList.java:851) at juc.concurrentmap.CopyOnWriteArrayListThread.run(CopyOnWriteArrayListTest.java:46) at java.lang.Thread.run(Thread.java:748)
! 2- analysis of the problem: the reason is still modCount = expectedModCount cause; so to understand modCount parameters belong to the object list, namely 10 threads a public modCount; but expectedModCount belong Iterator object list, and the list of Iterator objects in each new thread , recreated; the expectedModCount each thread are independent of each other, it will inevitably lead to "public variable" modCount not equal to the expectedModCount.
3- Solution: Use CopyOnWriteArrayList recording list data, and the data is removed using list.remove (str);
class CopyOnWriteArrayListThread implements Runnable{ //方式2 public static CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<String>(); static{ list.add("AA"); list.add("BB"); list.add("CC"); } @Override public void run() { Iterator<String> iterator = list.iterator(); while(iterator.hasNext()){ String str = iterator.next(); System.out.println(str); if(str.equals("CC")){ list.remove(str); } } } }
Third, the CopyOnWriteArrayList
What 1-CopyOnWriteArrayList that?
According to the official notes: CopyOnWriteArrayList is thread-safe ArrayList, their corresponding add, remove and other modification operations will also create a new copy
How do thread-safe 2-CopyOnWriteArrayList
add () and remove (): CopyOnWriteArrayList class greatest feature is, after the operation to modify (add / remove, etc.) and creates a new modification data, examples thereof modification is completed, then the original reference point to the new array. Thus, the modification process does not modify the original array. There will be no ConcurrentModificationException error.
//CopyOnWriteArrayList中的add方法 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(); } } //CopyOnWriteArrayList中的remove方法,调用下面的 remove(o, snapshot, index) public boolean remove(Object o) { Object[] snapshot = getArray(); int index = indexOf(o, snapshot, 0, snapshot.length); return (index < 0) ? false : remove(o, snapshot, index); } //真正的remove方法 private boolean remove(Object o, Object[] snapshot, int index) { final ReentrantLock lock = this.lock; lock.lock(); try { Object[] current = getArray(); int len = current.length; if (snapshot != current) findIndex: { int prefix = Math.min(index, len); for (int i = 0; i < prefix; i++) { if (current[i] != snapshot[i] && eq(o, current[i])) { index = i; break findIndex; } } if (index >= len) return false; if (current[index] == o) break findIndex; index = indexOf(o, current, index, len); if (index < 0) return false; } Object[] newElements = new Object[len - 1]; System.arraycopy(current, 0, newElements, 0, index); System.arraycopy(current, index + 1, newElements, index, len - index - 1); setArray(newElements); return true; } finally { lock.unlock(); } }
get()
//CopyOnWriteArrayList中的get方法 public E get(int index) { final ReentrantLock lock = l.lock; lock.lock(); try { rangeCheck(index); checkForComodification(); return l.get(index+offset); } finally { lock.unlock(); } }