La modification d'une Collection
itération while à l' Collection
aide de an Iterator
n'est pas autorisée par la plupart des Collection
classes. La bibliothèque Java appelle une tentative de modification d'un Collection
tout en l'itérant une "modification simultanée". Cela suggère malheureusement que la seule cause possible est la modification simultanée par plusieurs threads, mais ce n'est pas le cas. En utilisant un seul thread, il est possible de créer un itérateur pour Collection
(en utilisant Collection.iterator()
, ou une boucle amélioréefor
), de démarrer l'itération (en utilisant Iterator.next()
ou en entrant de manière équivalente le corps de la for
boucle améliorée ), de modifier le Collection
, puis de continuer l'itération.
Pour aider les programmeurs, certaines implémentations de ces Collection
classes tentent de détecter une modification concurrente erronée et lancent un ConcurrentModificationException
si elles la détectent. Cependant, il n'est en général pas possible et pratique de garantir la détection de toutes les modifications concurrentes. Ainsi, une utilisation erronée de l ' Collection
n'entraîne pas toujours un jet ConcurrentModificationException
.
La documentation de ConcurrentModificationException
dit:
Cette exception peut être levée par des méthodes qui ont détecté une modification concurrente d'un objet lorsqu'une telle modification n'est pas autorisée ...
Notez que cette exception n'indique pas toujours qu'un objet a été modifié simultanément par un thread différent. Si un seul thread émet une séquence d'appels de méthode qui viole le contrat d'un objet, l'objet peut lever cette exception ...
Notez que le comportement à échec rapide ne peut pas être garanti car il est, d'une manière générale, impossible de faire des garanties fermes en présence de modifications concurrentes non synchronisées. Les opérations échouées sont basées ConcurrentModificationException
sur le meilleur effort.
Notez que
La documentation du HashSet
, HashMap
, TreeSet
et les ArrayList
classes dit ceci:
Les itérateurs retournés [directement ou indirectement de cette classe] sont rapides: si la [collection] est modifiée à tout moment après la création de l'itérateur, de quelque manière que ce soit sauf via la propre méthode remove de l'itérateur, la commande Iterator
renvoie un ConcurrentModificationException
. Ainsi, face à une modification concurrente, l'itérateur échoue rapidement et proprement, plutôt que de risquer un comportement arbitraire et non déterministe à un moment indéterminé dans le futur.
Notez que le comportement de défaillance rapide d'un itérateur ne peut pas être garanti car il est, d'une manière générale, impossible de faire des garanties matérielles en présence de modifications concurrentes non synchronisées. Les itérateurs rapides échouent ConcurrentModificationException
sur la base du meilleur effort. Par conséquent, il serait erroné d'écrire un programme qui dépendait de cette exception pour son exactitude: le comportement rapide des itérateurs ne devrait être utilisé que pour détecter les bogues .
Notez à nouveau que le comportement «ne peut être garanti» et n'est que «au mieux».
La documentation de plusieurs méthodes de l' Map
interface dit ceci:
Les implémentations non simultanées devraient remplacer cette méthode et, au mieux, lancer un ConcurrentModificationException
s'il est détecté que la fonction de mappage modifie cette carte pendant le calcul. Les implémentations concurrentes doivent remplacer cette méthode et, au mieux, lancer un IllegalStateException
s'il est détecté que la fonction de mappage modifie cette carte pendant le calcul et que, par conséquent, le calcul ne se terminerait jamais.
Notez à nouveau que seule une "base de meilleur effort" est requise pour la détection, et a ConcurrentModificationException
n'est explicitement suggéré que pour les classes non concurrentes (non thread-safe).
Débogage ConcurrentModificationException
Ainsi, lorsque vous voyez une trace de pile due à a ConcurrentModificationException
, vous ne pouvez pas immédiatement supposer que la cause est un accès multithread non sécurisé à un fichier Collection
. Vous devez examiner la trace de pile pour déterminer quelle classe de a Collection
lancé l'exception (une méthode de la classe l'aura directement ou indirectement lancée), et pour quel Collection
objet. Ensuite, vous devez examiner d'où cet objet peut être modifié.
- La cause la plus fréquente est la modification de l'
Collection
intérieur d'une for
boucle améliorée sur le Collection
. Ce n'est pas parce que vous ne voyez pas d' Iterator
objet dans votre code source qu'il n'y en a pas Iterator
! Heureusement, l'une des instructions de la for
boucle défectueuse se trouve généralement dans la trace de la pile, il est donc généralement facile de localiser l'erreur.
- Un cas plus délicat est celui où votre code passe autour des références à l'
Collection
objet. Notez que les vues non modifiables des collections (telles que produites par Collections.unmodifiableList()
) conservent une référence à la collection modifiable, donc l' itération sur une collection "non modifiable" peut lever l'exception (la modification a été faite ailleurs). D'autres vues de votre Collection
, telles que les sous-listes , Map
les jeux d'entrées et Map
les jeux de clés conservent également des références à l'original (modifiable) Collection
. Cela peut être un problème même pour un thread-safe Collection
, tel que CopyOnWriteList
; ne supposez pas que les collections thread-safe (simultanées) ne peuvent jamais lever l'exception.
- Les opérations qui peuvent modifier un
Collection
peuvent être inattendues dans certains cas. Par exemple, LinkedHashMap.get()
modifie sa collection .
- Les cas les plus difficiles sont ceux où l'exception est due à une modification simultanée par plusieurs threads.
Programmation pour éviter les erreurs de modification simultanées
Lorsque cela est possible, limitez toutes les références à un Collection
objet, il est donc plus facile d'empêcher les modifications simultanées. Créez Collection
un private
objet ou une variable locale et ne renvoyez pas de références à Collection
ou à ses itérateurs à partir des méthodes. Il est alors beaucoup plus facile d'examiner tous les endroits où le Collection
peut être modifié. Si le Collection
doit être utilisé par plusieurs threads, il est alors pratique de s'assurer que les threads accèdent au Collection
seul avec une synchronisation et un verrouillage appropriés.