Les Modifications Simultanées Exception
Je suis actuellement en train de travailler sur une application multi-thread, et il m'arrive parfois de recevoir simultanément modification exception (environ une fois ou deux fois par heure en moyenne, mais survenant à des intervalles apparemment aléatoires).
La mauvaise classe est essentiellement un wrapper pour une carte -- qui s'étend LinkedHashMap
(avec accessOrder la valeur true). La classe dispose de quelques méthodes:
synchronized set(SomeKey key, SomeValue val)
La méthode set ajoute une paire clé/valeur à l'intérieur de la carte, et est protégé par le mot-clé synchronized.
synchronized get(SomeKey key)
La méthode get renvoie la valeur basée sur la touche d'entrée.
rebuild()
La carte interne est reconstruite une fois dans un certain temps (environ toutes les 2 minutes, les intervalles ne correspondent pas avec les exceptions). La reconstruction de la méthode essentiellement reconstruit les valeurs en fonction de leurs clés. Depuis la reconstruction() est assez cher, je n'ai pas mis un mot-clé synchronized sur la méthode. Au lieu de cela, je suis en train de faire:
public void rebuild(){
/* initialization stuff */
List<SomeKey> keysCopy = new ArrayList<SomeKey>();
synchronized (this) {
keysCopy.addAll(internalMap.keySet());
}
/*
do stuff with keysCopy, update a temporary map
*/
synchronized (this) {
internalMap.putAll(tempMap);
}
}
L'exception se produit à
keysCopy.addAll(internalMap.keySet());
À l'intérieur du bloc synchronisé.
Suggestions sont grandement appréciés. Se sentir libre pour me pointer vers des pages ou chapitres de Efficace Java et/ou de la Simultanéité dans la Pratique.
Mise à jour 1:
Aseptisé stacktrace:
java.util.ConcurrentModificationException
at java.util.LinkedHashMap$LinkedHashIterator.nextEntry(LinkedHashMap.java:365)
at java.util.LinkedHashMap$KeyIterator.next(LinkedHashMap.java:376)
at java.util.AbstractCollection.toArray(AbstractCollection.java:126)
at java.util.ArrayList.addAll(ArrayList.java:473)
at a.b.c.etc.SomeWrapper.rebuild(SomeWraper.java:109)
at a.b.c.etc.SomeCaller.updateCache(SomeCaller.java:421)
...
Mise à jour 2:
Merci à tous pour les réponses. Je pense que le problème réside dans le LinkedHashMap et ses accessOrder attribut, bien que je ne suis pas tout à fait certain de l'atm (enquête).
Si accessOrder sur une LinkedHashMap est définie à vrai, et je accéder à ses keySet ensuite ajoutez le jeu de clés à une linkedList via addAll, faire une de ces actions muter de la commande (compter pour un "accès")?
Oui, vous avez absolument raison; mais, selon la conception de documents, il est acceptable d'avoir un peu rassis de données 🙂
Merde, je souhaite que j'avais conception docs comme ça 😉
Désolé, mais pouvez-vous montrer à la déclaration et l'initialisation de la internalMap?
pourriez-vous également de montrer que ConcurrentModificationException pile
OriginalL'auteur Cambium | 2009-07-06
Vous devez vous connecter pour publier un commentaire.
Si vous construit LinkedHashMap avec accessOrder = vrai alors LinkedHashMap.get() en fait mute le LinkedHashMap car il stocke le plus récemment accédé à l'entrée à l'avant de la liste des entrées. Peut-être que quelque chose est l'appel de get (), tandis que le tableau de la liste est de rendre sa copie avec l'Itérateur.
Basé sur votre commentaire ci-dessus que vous utilisez accessOrder, et en supposant que vous avez d'appeler get() dans la fonction de reconstruction (puisque tout vous montrer, c'est l'accaparement des touches), il semble probable. Effectuer la reconstruction() appelle jamais se chevauchent?
J'imagine méthodes comme containsKey() ou containsValue() serait également muter la carte, mais la documentation pour LinkedHashMap est silencieuse sur cette question.
OriginalL'auteur Sean McCauliff
Cette exception n'ont normalement rien à voir avec la synchronisation, il est normalement générée si une Collection est modifiée alors qu'un Itérateur est une itération à travers elle. AddAll méthodes peuvent utiliser un itérateur - et il est intéressant de noter que le chic boucle foreach parcourt les instances de l'Itérateur.
e.g:
est suffisant pour obtenir de l'exception, sur certaines collections (e.g ArrayList).
James
C'est en fait le seul moyen pour que cette exception d' - stackoverflow.com/questions/602636/...
Il est pertinent parce que l'exception est levée à partir de votre code presque certainement de la manière que je décris.
OriginalL'auteur gubby
Sont ceux de toutes les fonctions que vous avez dans votre enveloppe? Parce que cette exception peut être levée pendant que vous en quelque sorte une itération sur collection dans un autre endroit. Et je suppose que vous méthode synchronisée avec un potentiel évident condition de course, mais peut-être manqué de moins en moins évidente cas. Ici la référence à la classe d'exception docs.
Est-il de code dans votre projet qui utilise un itérateur pour cette classe? Sont ceux qui utilise correctement synchronisé?
Cette classe n'a pas d'itérateur. L'interne de la carte peut seulement être modifié par les trois méthodes que j'ai énumérés ci-dessus.
OriginalL'auteur Artem Barger
De la Javadoc:
Il peut être plus sûr pour vous de réellement envelopper le LinkedHashMap plutôt que de réclamer de vous étendre. Si votre application pourrait avoir à l'intérieur un membre de données qui est la Carte retournée par Collections.synchronizedMap(nouveau LinkedHashMap(...)).
Veuillez voir les Collections de javadoc pour plus de détails: Les Collections.synchronizedMap
OriginalL'auteur Tim Bender
Essayez de supprimer le mot-clé synchronized du set() et get() les méthodes, et au lieu d'utiliser un bloc synchronisé à l'intérieur de la méthode, le verrouillage de internalMap; puis modifiez la synchronisation des blocs sur la reconstruction() la méthode de verrouillage sur le internalMap.
Étant donné que la carte interne ne peut être accessible via le wrapper changement de la serrure ne fera aucune différence. Il vous suffira de déplacer le texte autour de pour aucun avantage.
OriginalL'auteur Chochos
que c'est étrange. Je ne peux pas le voir de toute façon que l'exception est levée à l'emplacement où vous identifier (dans le défaut de mise en oeuvre en Java 6). êtes-vous d'obtenir un ConcurrentModificationException dans la liste de tableaux ou de la table de hachage? avez-vous remplacé le
keySet()
méthode dans votreHashMap
?modifier mon erreur - la liste de tableaux force le jeu de clés pour effectuer une itération (AbstractCollection.toArray() pour itérer sur les touches)
il y a quelque part dans le code vous permettant de mettre à jour votre carte interne qui n'est pas synchronisé.
Je suis entièrement d'accord avec votre analyse, cependant, je triple/quadruple vérifié ma classe, et fait en sorte que ces trois méthodes sont les seules les accesseurs de la carte interne; et bien sûr, la carte interne est privé.
OriginalL'auteur akf
Est internalMap statique? Vous pouvez avoir plusieurs objets, chacun de verrouillage sur
this
, l'objet, mais ne fournissant pas de corriger le verrouillage de votre statique internalMap.Nope, pas statique.
OriginalL'auteur Stephen Denne
Essayer de déclarer la Carte transitoire
OriginalL'auteur Reed
Lors de l'itération sur les touches, en raison de
Décidez-vous que certaines entrées peuvent être supprimées de la LinkedHashMap?
La removeEldestEntry méthode pourrait être soit de retourner true ou de modification de la carte, ce qui bouleverse l'itération.
OriginalL'auteur Stephen Denne
Je ne pense pas que votre
set()
/get()
sont synchronisées avec le même moniteur querebuild()
est. Cela rend-il possible pour quelqu'un d'appeler set/get alors que la problématique de la ligne est en cours d'exécution, spécialement lors d'une itération de plus ils ont un jeu de clés de la internalMap au cours de laaddAll()
appel (mise en œuvre interne qui est exposée par le biais de votre stack trace).Au lieu de faire
set()
synchronisée, avez-vous essayé quelque chose le long des lignes de:Je ne pense pas que vous avez besoin de synchroniser
get()
à tous, mais si vous insistez:En fait, je pense que vous feriez mieux de synchronisation contre
internalMap
au lieu dethis
. Il ne fait pas de mal de faireinternalMap
volatile
, bien que je ne pense pas que ce soit vraiment nécessaire si vous êtes sûr queset()
/get()
/rebuild()
sont les seules méthodes pour accéder directement à internalMap, et ils sont tous d'y accéder de manière synchronisée.OriginalL'auteur Jack Leow
essayez ceci:
OriginalL'auteur Oso
Garder l'accès à internalMap synchronisées, autrement java.util.ConcurrentModificationException se produit parce que HashMap#modCount (qui enregistre les changements structurels) peut être changé en parallèle au cours de clavier itération (en raison keysCopy.addAll(internalMap.keySet() invocation).
LinkedHashMap javaDoc précise : "Dans l'accès ordonné lié hachage cartes, simplement l'interrogation de la carte avec obtenir est une modification structurelle."
OriginalL'auteur