étude Java approfondie 07: ConcurrentModificationException anormale et CopyOnWriteArrayList
D'abord, regardez un seul fil lu Iterator Traversal anormale
1- code est le suivant: Créer une liste de tableaux, et ajouter trois éléments, ouvrir un fil, traverse le ArrayList, en lisant les données, alors que la suppression de données
publique classe CopyOnWriteArrayListTest { publics statiques vides principaux (String [] args) { CopyOnWriteArrayListThread t = nouvelle CopyOnWriteArrayListThread (); pour ( int i = 0; i <1; i ++ ) { nouveau fil (t) .start (); } } } Classe CopyOnWriteArrayListThread outils Runnable { // 方式1 publique statique Liste <String> list = nouvelle ArrayList <String> (); statique { list.add ("AA" ); list.add ( "BB" ); list.add ( "CC" ); } @Override publique vide run () { Iterator <String> iterator = list.iterator (); tandis que (iterator.hasNext ()) { String str = iterator.next (); System.out.println (str); si (str.equals ( "CC" )) { list.remove (str); } } } }
2- Les résultats sont comme suit: Rapport d'exception ConcurrentModificationException
java.util. ConcurrentModificationException à java.util.ArrayList $ Itr.checkForComodification (ArrayList.java: 901 ) à java.util.ArrayList $ Itr.next (ArrayList.java: 851 ) à juc.concurrentmap.CopyOnWriteArrayListThread.run (CopyOnWriteArrayListTest.java: 42 ) à java.lang.Thread.run (Thread.java: 748)
3- causes d'anomalie ConcurrentModificationException;
Lorsque la méthode exécution iterator.next (), nous procéderons à un jugement checkForComodification interne (), quand ils jettent ConcurrentModificationException modCount = expectedModCount quand anormale !;
Dans lequel, lorsque le nombre de fois ModCount ArrayList modifié enregistrer, de modifier les attentes nombre expectedModCount ArrayList est, sa valeur initiale ModCount;
list.remove à l'intérieur (str) opération effectuée modCount ++, mais expectedModCount et la quantité de riz est mis à jour, de sorte que l'erreur;
publique suivant E () { checkForComodification (); int i = curseur; si (i> = taille) jette nouvelle NoSuchElementException (); Object [] elementData = ArrayList. ce .elementData; si (i> = elementData.length) lancer de nouveaux ConcurrentModificationException (); curseur = i + 1 ; retour (E) elementData [lastRet = i]; } Finale vide checkForComodification () { si (modCount! =expectedModCount) lancer de nouveaux ConcurrentModificationException (); }
4 la solution mono-thread: changement list.remove (str) est Iterator.remove (); ArrayList méthode itérateur de suppression, dans lequel la valeur mise à jour expectedModCount
@Override publique vide run () { Iterator <String> iterator = list.iterator (); tandis que (iterator.hasNext ()) { String str = iterator.next (); System.out.println (str); si (str.equals ( "CC" )) { Iterator.remove (); } } }
privé classe Itr outils Iterator <E> { // ArrayList中的Iterator supprimer方法 publique vide remove () { si (lastRet <0 ) lancer de nouveaux IllegalStateException (); checkForComodification (); essayez { ArrayList. ce .remove (lastRet); curseur = lastRet; lastRet = -1 ; expectedModCount = modCount; } captures(IndexOutOfBoundsException ex) { jeter nouveau ConcurrentModificationException (); } } }
En second lieu, multithread, même si Iterator.remove (), encore rapporté ConcurrentModificationException;
1- code est le suivant: créer une liste de tableaux, et à trois éléments, ouvert 10 fils, parcourir la liste de tableaux, tandis que la lecture des données, lors de la suppression des données
publique classe CopyOnWriteArrayListTest { publics statiques vides principaux (String [] args) { CopyOnWriteArrayListThread t = nouvelle CopyOnWriteArrayListThread (); pour ( int i = 0; i < 10 ; i ++ ) { nouveau fil (t) .start (); } } } Classe CopyOnWriteArrayListThread outils Runnable { // 方式1 publique statique Liste <String> list = nouvelle ArrayList <String> (); statique { list.add ( "AA" ); list.add ( "BB" ); list.add ( "CC" ); } @Override publique vide run () { Iterator <String> iterator = list.iterator (); tandis que (iterator.hasNext ()) { String str = iterator.next (); System.out.println (str); si (str.equals ( "CC" )) { Iterator.remove (); } } } }
journal d'exception
java.util. ConcurrentModificationException à java.util.ArrayList $ Itr.checkForComodification (ArrayList.java: 901 ) à java.util.ArrayList $ Itr.next (ArrayList.java: 851 ) à juc.concurrentmap.CopyOnWriteArrayListThread.run (CopyOnWriteArrayListTest.java: 46 ) à java.lang.Thread.run (Thread.java: 748)
! 2- Analyse du problème: la raison est encore modCount = expectedModCount cause, afin de comprendre les paramètres modCount appartiennent à la liste des objets, à savoir 10 fils d'un modCount public, mais expectedModCount appartiennent liste des objets Iterator, et la liste des objets Iterator dans chaque nouveau fil , recréée, le expectedModCount chaque fil sont indépendants les uns des autres, il va inévitablement conduire à « variable publique » modCount pas égale à la expectedModCount.
3- Solution: Utiliser des données de liste d'enregistrement CopyOnWriteArrayList, et les données sont supprimées en utilisant list.remove (str);
classe CopyOnWriteArrayListThread de Runnable { // 方式2 CopyOnWriteArrayList de public static <String> Liste = new CopyOnWriteArrayList <String> (); statique { list.add ( "AA" ); list.add ( "BB" ); list.add ( "CC" ); } @Override publique vide run () { Iterator <String> iterator = list.iterator (); tandis que (iterator.hasNext ()) { String str = iterator.next (); System.out.println (str); si (str.equals ( "CC" )) { list.remove (str); } } } }
En troisième lieu, le CopyOnWriteArrayList
Qu'est-ce que 1-CopyOnWriteArrayList qui?
Selon les notes officielles: CopyOnWriteArrayList est ArrayList thread-safe, leur ajouter correspondant, supprimer et d'autres opérations de modification permettra également de créer une nouvelle copie
Comment 2-CopyOnWriteArrayList thread-safe
ajouter () et remove (): CopyOnWriteArrayList plus grande caractéristique de la classe est, après l'opération à modifier (ajouter / supprimer, etc.) et crée une nouvelle données de modification, des exemples de celle-ci la modification est terminée, le point de référence original au nouveau tableau. Ainsi, le processus de modification ne modifie pas le tableau original. Il n'y aura pas d'erreur de ConcurrentModificationException.
// CopyOnWriteArrayList中的ajouter方法 publique booléen add (E e) { finale verrouillage de ReentrantLock = ce .lock; serrure.Serrure (); essayez { Object [] éléments = GetArray (); int len = elements.length; Object [] = newElements Arrays.copyOf (éléments, len + 1); newElements [len] = e; setArray (newElements); retour vrai ; } Enfin { lock.unlock (); } } //CopyOnWriteArrayList中的supprimer方法,调用下面的supprimer (o, instantané, index) du public boolean remove (Object o) { Object [] instantané = getArray (); int index = indexOf (o, instantané, 0 , snapshot.length); retour (indice <0)? faux : supprimer (o, instantané, index); } // 真正的supprimer方法 privé booléenne supprimer (o Object, Object [] instantané, int index) { finale verrouillage de ReentrantLock = ce .lock; serrure.Serrure (); essayez { courant Object [] =getArray (); int len = current.length; si (instantané =! courant) FindIndex: { int préfixe = Math.min (index, len); pour ( int i = 0; i <préfixe; i ++ ) { si (courant [i] = instantané [i] &&! eq (o, courant [i])) { indice = i; rompre FindIndex; } } Si (index> = len) retour faux; si ([index] courant == o) rompre FindIndex; index = indexOf (o, courant, index, LEN); si (indice <0 ) retour faux ; } Object [] newElements = new Object [len - 1]; System.arraycopy (courant, 0, 0, newElements, index); System.arraycopy (courant, index + 1, newElements, index, len - index - 1); setArray (newElements); retour vrai; } Enfin { lock.unlock (); } }
avoir()
// CopyOnWriteArrayList中的get方法 publique get E ( int index) { finale serrure ReentrantLock = l.lock; serrure.Serrure (); essayer { rangeCheck (index); checkForComodification (); retour l.get (index + offset); } Enfin { lock.unlock (); } }