Je travaille actuellement sur une application multi-threadée, et je reçois parfois une exception de modification simultanément (environ une ou deux heures en moyenne, mais survenant à des intervalles apparemment aléatoires).
La classe défectueuse est essentiellement un wrapper pour une carte - qui s'étend La méthode de réglage ajoute une paire de clé / valeur à la carte interne et est protégée par le mot-clé synchronisé. P> linkedhashmap code> (avec accessorposeur défini sur true). La classe a quelques méthodes: p>
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)
...
12 Réponses :
Essayez de supprimer le mot clé synchronisé des méthodes définies () et d'obtenir () et utilisez plutôt un bloc synchronisé à l'intérieur de la méthode, verrouillant sur internalmap; Changez ensuite les blocs synchronisés sur la méthode de reconstruction () pour verrouiller également le site internalap. P>
Merci pour la suggestion, pouvez-vous élaborer un peu pourquoi cela pourrait résoudre le problème? Je vais essayer bientôt.
Étant donné que la carte interne ne peut être accédée que via la modification de l'emballage de la serrure ne fera aucune différence. Vous allez simplement déplacer du texte pour aucun avantage.
Ces fonctionnaires sont-ils tous dans votre wrapper? Parce que cette exception pouvait être jetée pendant que vous itérients en quelque sorte sur la collecte dans un autre endroit. Et je suppose que vous avez synchronisé la méthode avec une condition de race évidente potentielle, mais vous avez probablement manqué des cas moins évidents. ici La référence aux documents de classe d'exception. p>
Oui, toutes ces méthodes sont dans la classe wrapper.
Y a-t-il un code dans votre projet qui utilise un itérateur pour cette classe? Ces utilisations sont-elles synchronisées correctement?
Cette classe n'a pas d'itérateur. La carte interne ne peut être modifiée que via les trois méthodes que j'ai énumérées ci-dessus.
c'est étrange. Je ne vois aucune façon que l'exception soit lancée à l'emplacement que vous identifiez (dans l'implication par défaut dans JAVA 6). Obtenez-vous une exception concyurentModification dans le tableau ou le hashmap? avez-vous remplacé la méthode code> Keyset () code> dans votre EDIT STRUT> Mon erreur - La arrayliste forcera le Keyset à itération (abstractcollection.toarray () ira déterrera sur ses clés) p>
Il y a quelque part dans le code qui vous permet de mettre à jour votre carte interne qui n'est pas synchronisée. p>
hashmap code>? p>
Non, Keyset () n'est pas remplacée. Je ne pense pas que cela entraînera un problème, car le seul moyen d'accéder à cette carte interne passe par le jeu (), obtenir (), obtenir () et reconstruire ().
Je suis tout à fait d'accord avec votre analyse, cependant, j'ai triplé / quadruple vérifié ma classe et nous sommes assurés que ces trois méthodes sont les seuls accessoires de la carte interne; Et bien sûr, la carte interne est privée.
Cette exception n'a généralement rien à voir avec la synchronisation - elle est normalement lancée si une collection est modifiée lorsqu'un itérateur est itérateur à travers elle. Les méthodes Addall peuvent utiliser un itérateur - et sa peine notant que la boucle de Posh Faach iTerate sur des instances d'itérateur.
par exemple: p> est suffisant pour obtenir l'exception certaines collections (par exemple, arraylist). P> James P> P>
Salut James, je ne vois pas comment cela est pertinent. Je ne pense pas que je modifie la carte / le keyset de la carte en itérant.
@Cambrium - C'est en fait la seule façon de se produire - Stackoverflow.com/Questtions/602636/...
Il est pertinent car l'exception est projetée de votre code presque certainement de la manière que je décris.
est interne statique? Vous pouvez avoir plusieurs objets, chaque verrouillage sur ceci code>, l'objet, mais ne fournissant pas de verrouillage correct sur votre internaute statique. P>
Si vous avez construit LinkedHashMap avec AccessOrder = True, LinkedHashMap.get () Mutate réellement le LinkedHashMap car il stocke l'entrée la plus récente accessible à l'avant de la liste liée des entrées. Peut-être que quelque chose appelle get () pendant que la liste des matrices effectue sa copie avec l'itérateur. P>
En supposant que personne ne appelle obtenir () alors qu'il fait la copie, cela pourrait-il toujours déclencher l'exception? Vous soulevez un très bon point, je vais examiner les implémentations LinkedHashMap. Merci!
Basé sur votre commentaire ci-dessus que vous utilisez AccessOrder, et vous supposez que vous devez appeler get () dans la fonction de reconstruction (puisque tout ce que vous montrez, vous attrapez des clés), cela semble un coupable probable. Est-ce que les appels de reconstruction () se chevauchent jamais?
J'imagine que des méthodes telles que Containkey () ou contenantValue () muteraient également la carte, mais la documentation pour LinkedHashMap est silencieuse sur ce problème.
Je ne pense pas que votre au lieu de créer Je ne pense pas Vous devez synchroniser En fait, je pense que vous feriez mieux de se synchroniser contre internalmap code> au lieu de Set () code> /
get () code> est synchronisé contre le même moniteur que
reconstruit () code> est. Cela permet à une personne d'appeler le jeu / gêner pendant que la ligne problématique est en cours d'exécution, en particulier lors de l'itération de l'ensemble clé de la clé de l'internaute lors de l'appel
Addall () CODE> APPEL (Mise en œuvre interne exposée via votre pile Trace).
SET () code> synchronisé, avez-vous essayé quelque chose dans le sens de: p>
get () code> du tout, mais si vous insistez: p>
ceci code>. Il n'a pas non plus mal de faire
internalmap code>
volatile code>, bien que je ne pense pas que cela soit vraiment nécessaire si vous êtes sûr que
set () code > /
get () code> /
reconstruit () code> sont les seules méthodes accessibles directement à internalmap, et elles y accédent tous de manière synchronisée. P>
private volatile Map internalMap ...
En itérant sur les touches, en raison de Décidez-vous que certaines des entrées peuvent être supprimées du LinkedHashMap? p> Le removeelDestEntry méthode pourrait être soit de retour vrai, soit la modification de la carte, contrarier ainsi l'itération. P> P>
Essayez de déclarer la carte comme transitoire p>
zut, je voulais dire volatile, car Jack mentionne ci-dessous
Essayez ceci:
public void rebuild(){ /* initialization stuff */ List<SomeKey> keysCopy = new ArrayList<SomeKey>(); synchronized (internalMap) { keysCopy.addAll(internalMap.keySet()); } /* do stuff with keysCopy, update a temporary map */ synchronized (internalMap) { internalMap.putAll(tempMap); } }
du Javadoc: P>
Si plusieurs threads accèdent simultanément une carte de hachage liée, et au moins une des threads modifie la carte structurellement, elle doit être synchronisée à l'extérieur. Ceci est généralement accompli en se synchronisant sur certains objets qui encapsulate naturellement la carte. Si aucun objet de ce type n'existe, la carte doit être "enveloppée" à l'aide de la méthode Collections.SynchronizedMap. Ceci est mieux fait au moment de la création, pour empêcher l'accès accidentel non synchronisé à la carte: P>
mappe m = collections.synchronizedMap (nouveau LinkedHashMap (...)); P> blockQuote>
Il peut être plus sûr que vous puissiez réellement envelopper le LinkedHashMap plutôt que de prétendre que vous l'étendez. Votre implémentation aurait donc un élément de données interne qui est la carte renvoyée par Collections.SynchronizedMap (nouveau LinkedHashMap (...)). P>
S'il vous plaît voir les collections Javadoc pour plus de détails: Collections.SynchronizedMap P>
Gardez l'accès à internalmap Syncronisé fort>, sinon Java.Util.ConCurrentModificationException a lieu car hashmap # modcount (qui enregistre les modifications structurelles) peut être modifiée simultanément pendant le keyset itération (attelé Keyscopy.Addall (internalmap.keyset ( ) Invocation). P>
LinkedHashMap Javadoc Spécifie: "Dans les cartes de hachage liées autorisées par accès, interroge simplement la carte avec
OT, mais ne vous risquez pas de manquer de modifications à la classe dans la région commentée entre les deux méthodes synchronisées de reconstruction ()?
Oui, vous avez absolument raison; Mais selon les documents du design, il est acceptable d'avoir des données quelque peu étabes :)
Putain, j'aimerais avoir des documents conçus comme ça;)
Désolé, pouvez-vous montrer la déclaration et l'initialisation de l'internaute?
Pourriez-vous également montrer cette pile de la concurrence
@Tom: Désolé, je ne peux pas vraiment divulguer la mise en œuvre de la classe "internalap". J'espère que tu peux comprendre :)
@ AKF: J'ai assainusé la stacktrace un peu, j'espère que c'est suffisant: java.util.concurrentModificationException à Java.Util.LinkedHashMap $ linkedhasteratorator.nextentry (linkedh ashmap.java:365) at java.util.linkedhashmap $ keyiterator.next ( Linkedhashmap.java: 376) à Java.Util.AbstractCollection.Tearray (abstractcollection.java: 126) à java.util.arraylist.addall (arraylist.java:473) à abcetc.somewrapper.rebuild (onewraper.java : 109) à abcetc.somecaller.updateCache (somecaller.java:421) ...
Modifiez-vous l'ensemble dans un autre fil, par exemple Ajout / suppression d'éléments en réponse à un message reçu sur le réseau?
@Cambium: Je comprends s'il y a des choses exclusives ... mais j'avais du mal à comprendre. Votre carte synchronisée n'est-elle pas une collection simultanée (qui vient standard en Java)? Est-ce que InternestMap est juste une carte régulière? Ou est-ce une carte simultanée? Ou est-ce une classe exclusive?
@Finnw Non, je ne modifie pas la carte interne n'importe où. La carte interne est privée et on ne peut être accédé que via les trois méthodes que j'ai fournies. @ Tom La classe internalmap étend hachmap, mais la plupart des procédés (j'ai utilisés) sont la nervation (à l'exception de Keyset ()) et protégées avec le mot-clé synchronisé.
Eh bien, c'est difficile pour moi de dire si ... mais voici ce que je pense que le problème est. Je ne peux pas dire si tous les accès à internalmap se synchronisent finalement sur la carte. Mais je me demande si au lieu d'une synchronisation (ceci), vous avez besoin d'une synchronisation (internalmap). Il est possible que les deux puissent être nécessaires. En fin de compte, cela semble être un mauvais design, mais j'imagine que l'erreur provient d'une synchronisation inappropriée ailleurs. Je pense que quelqu'un d'autre a une poignée à internalmap et l'utilise pendant que vous le copiez. C'est mauvais et le moyen le plus simple de dire si c'est le problème consiste à ajouter synchronisé (internalmap).
Salut Tom, merci pour les réponses. Je pense que j'ai négligé des trucs pour la conception internalmap, et il prolonge en fait LinkedHashMap (au lieu de HASHMAP) et définit AccessOrder à vrai. Tout en prétendant être "fil-coffre-fort", son comportement diffère de mes attentes, ce qui a provoqué l'exception. J'essaie de travailler autour de ce guichet automatique. Voir la réponse de Sean: Stackoverflow.com/Questions/1089546/.../ a>