8
votes

Exception de modification simultanée

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 linkedhashmap code> (avec accessorposeur défini sur true). La classe a quelques méthodes: p> xxx pré>

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>

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 commentaires

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/...


12 Réponses :


0
votes

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.


2 commentaires

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.



2
votes

3 commentaires

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.



0
votes

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 Keyset () dans votre hashmap ?

EDIT Mon erreur - La arrayliste forcera le Keyset à itération (abstractcollection.toarray () ira déterrera sur ses clés)

Il y a quelque part dans le code qui vous permet de mettre à jour votre carte interne qui n'est pas synchronisée.

  • Avez-vous une méthode utilitaire qui expose votre carte interne qui "ne devrait pas être utilisée" ou
  • Votre carte interne est-elle identifiée comme une portée publique

2 commentaires

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.



6
votes

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: xxx

est suffisant pour obtenir l'exception certaines collections (par exemple, arraylist).

James


3 commentaires

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.



0
votes

est interne statique? Vous pouvez avoir plusieurs objets, chaque verrouillage sur ceci , l'objet, mais ne fournissant pas de verrouillage correct sur votre internaute statique.


0 commentaires

7
votes

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.


3 commentaires

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.



0
votes

Je ne pense pas que votre 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).

au lieu de créer SET () code> synchronisé, avez-vous essayé quelque chose dans le sens de: p> xxx pré>

Je ne pense pas Vous devez synchroniser get () code> du tout, mais si vous insistez: p> xxx pré>

En fait, je pense que vous feriez mieux de se synchroniser contre internalmap code> au lieu de 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 ...


0 commentaires

0
votes

En itérant sur les touches, en raison de xxx

Décidez-vous que certaines des entrées peuvent être supprimées du LinkedHashMap?

Le removeelDestEntry méthode pourrait être soit de retour vrai, soit la modification de la carte, contrarier ainsi l'itération.


0 commentaires

0
votes

Essayez de déclarer la carte comme transitoire


1 commentaires

zut, je voulais dire volatile, car Jack mentionne ci-dessous



0
votes

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);
  }
}


0 commentaires

1
votes

du Javadoc:

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:

mappe m = collections.synchronizedMap (nouveau LinkedHashMap (...));

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 (...)).

S'il vous plaît voir les collections Javadoc pour plus de détails: Collections.SynchronizedMap


0 commentaires

0
votes

Gardez l'accès à internalmap Syncronisé , 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).

LinkedHashMap Javadoc Spécifie: "Dans les cartes de hachage liées autorisées par accès, interroge simplement la carte avec get est une modification structurelle".


0 commentaires