Question

Dans une application multithread sur laquelle je travaille, nous voyons parfois ConcurrentModificationExceptions sur nos listes (qui sont principalement ArrayList , parfois des vecteurs). Mais il y a d'autres moments où je pense que des modifications simultanées sont en train de se produire car il semble qu'il manque des éléments dans la collection, mais aucune exception n'est levée. Je sais que la documentation pour ConcurrentModificationException indique que vous ne pouvez pas vous en fier, mais comment puis-je m'assurer que je ne modifie pas simultanément une liste? Et encapsuler chaque accès à la collection dans un bloc synchronisé est-il le seul moyen de l’empêcher?

Mise à jour: Oui, je suis au courant de Collections.synchronizedCollection , mais cela ne protège pas contre la modification de la collection par une personne lorsque vous la parcourez. Je pense qu'au moins une partie de mon problème se produit lorsque quelqu'un ajoute quelque chose à une collection pendant que je le parcoure.

Deuxième mise à jour Si quelqu'un souhaite associer la mention synchronCollection et le clonage comme Jason l'a fait avec une mention des structures de collections java.util.concurrent et apache telles que jacekfoo et Javamann, je peux le faire. accepter une réponse.

Était-ce utile?

La solution

Votre question initiale semble demander un itérateur qui voit les mises à jour en direct de la collection sous-jacente tout en respectant les threads. C’est un problème extrêmement coûteux à résoudre dans le cas général. C’est pourquoi aucune des classes de collection standard ne le fait.

Il existe de nombreux moyens de trouver des solutions partielles au problème et, dans votre application, l'un d'entre eux peut suffire.

Jason fournit à un moyen spécifique d'assurer la sécurité des threads et d'éviter de déclencher une exception ConcurrentModificationException . mais seulement au détriment de la vie.

Javamann mentionne deux classes spécifiques dans le java.util.concurrent package qui résout le même problème de manière sécurisée, où l'évolutivité est essentielle. Ceux-ci sont uniquement livrés avec Java 5, mais divers projets ont été introduits dans les versions précédentes de Java, notamment celui-ci , même s’ils n’ont pas de telles performances dans les JRE précédents.

Si vous utilisez déjà certaines des bibliothèques Apache Commons, alors, comme le souligne jacekfoo, , le framework de collections apache contient des classes utiles.

Vous pouvez également consulter le référentiel Google sur les collections .

Autres conseils

En fonction de la fréquence de vos mises à jour, l'un de mes favoris est CopyOnWriteArrayList ou CopyOnWriteArraySet. Ils créent une nouvelle liste / définition sur les mises à jour pour éviter une exception de modification simultanée.

Découvrez java.util.concurrent pour les versions des classes de collections standard conçues pour mieux gérer la concurrence.

Oui, vous devez synchroniser l'accès aux objets de collections.

Vous pouvez également utiliser les wrappers synchronisés autour de tout objet existant. Voir Collections. synchronizedCollection () . Par exemple:

List<String> safeList = Collections.synchronizedList( originalList );

Cependant, tout le code doit utiliser la version sécurisée, et même si une itération pendant qu'un autre thread le modifie entraîne des problèmes.

Pour résoudre le problème d'itération, copiez d'abord la liste. Exemple:

for ( String el : safeList.clone() )
{ ... }

Pour des collections optimisées et sécurisées pour les threads, consultez également java.util.concurrent .

Généralement, vous obtenez une exception ConcurrentModificationException si vous essayez de supprimer un élément d'une liste pendant son itération.

Le moyen le plus simple de tester ceci est:

List<Blah> list = new ArrayList<Blah>();
for (Blah blah : list) {
     list.remove(blah); // will throw the exception
}

Je ne sais pas comment vous pourriez vous en sortir. Vous devrez peut-être implémenter votre propre liste thread-safe ou créer des copies de la liste d'origine pour l'écriture et disposer d'une classe synchronisée pour écrire dans la liste.

Vous pouvez essayer d'utiliser la copie défensive de sorte que les modifications apportées à une liste n'affectent pas les autres.

La méthode appropriée consiste à regrouper les accès à la collection dans un bloc synchronisé. La pratique de la programmation standard dicte l’utilisation d’un mécanisme de verrouillage (sémaphore, mutex, etc.) lorsqu’il s’agit d’un état partagé par plusieurs threads.

En fonction de votre cas d'utilisation, vous pouvez toutefois procéder à certaines optimisations pour verrouiller uniquement dans certains cas. Par exemple, si vous avez une collection fréquemment lue mais rarement écrite, vous pouvez autoriser les lectures simultanées mais imposer un verrou chaque fois qu'une écriture est en cours. Les lectures simultanées ne provoquent des conflits que si la collection est en cours de modification.

ConcurrentModificationException constitue le meilleur effort possible, car ce que vous demandez est un problème complexe. Il n’est pas possible de faire cela de manière fiable sans sacrifier les performances, en plus de prouver que vos modèles d’accès ne modifient pas la liste simultanément.

La synchronisation empêcherait probablement les modifications simultanées. C’est peut-être à quoi vous aurez recours à la fin, mais elle risque d’être coûteuse. La meilleure chose à faire est probablement de vous asseoir et de réfléchir pendant un moment à votre algorithme. Si vous ne pouvez pas proposer de solution sans verrouillage, utilisez la synchronisation.

Voir la mise en œuvre. Il stocke essentiellement un int:

transient volatile int modCount;

et qui est incrémenté lorsqu’il y a une 'modification structurelle' (comme remove). Si l'itérateur détecte que modCount a changé, il lève une exception de modification simultanée.

La synchronisation (via Collections.synchronizedXXX) n’est pas rentable car elle ne garantit pas la sécurité des itérateurs, elle ne fait que synchroniser les écritures et les lectures via put, get, set ...

Voir les frameworks de collections java.util.concurennt et apache (certaines classes optimisées fonctionnent correctement dans un environnement simultané lorsque le nombre de lectures (non synchronisées) est supérieur au nombre d'écritures - voir FastHashMap.

Vous pouvez également synchroniser des itératines sur la liste.

List<String> safeList = Collections.synchronizedList( originalList );

public void doSomething() {
   synchronized(safeList){
     for(String s : safeList){
           System.out.println(s);

     }
   }

}

Ceci verrouillera la liste lors de la synchronisation et bloquera tous les threads qui tentent d'accéder à la liste pendant que vous la modifiez ou la parcourez. L'inconvénient est que vous créez un goulot d'étranglement.

Ceci économise de la mémoire sur la méthode .clone () et pourrait être plus rapide en fonction de ce que vous faites dans l'itération ...

Collections.synchronizedList () rendra une liste nominalement thread-safe et java.util.concurrent a des fonctionnalités plus puissantes.

Ceci éliminera votre exception de modification simultanée. Je ne parlerai cependant pas de l'efficacité;)

List<Blah> list = fillMyList();
List<Blah> temp = new ArrayList<Blah>();
for (Blah blah : list) {
     //list.remove(blah);  would throw the exception
     temp.add(blah);
}
list.removeAll(temp);
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top