web-dev-qa-db-fra.com

Liste synchronisée Java

J'ai une liste de tableau pré-remplie. Et j'ai plusieurs threads qui vont supprimer des éléments de la liste de tableaux. Chaque thread appelle la méthode remove ci-dessous et supprime un élément de la liste. Le code suivant me donne-t-il un comportement cohérent?

ArrayList<String> list = Collections.synchronizedList(new ArrayList<String>());

void remove(String item)
{
     do something; (doesn't work on the list)
     list.remove(item);
}

Merci!

60
shadowfax

Oui, faites attention si vous parcourez également la liste, car dans ce cas, vous devrez vous synchroniser dessus. Depuis le Javadoc :

Il est impératif que l'utilisateur se synchronise manuellement sur la liste renvoyée lors de son itération:

List list = Collections.synchronizedList(new ArrayList());
    ...
synchronized (list) {
    Iterator i = list.iterator(); // Must be in synchronized block
    while (i.hasNext())
        foo(i.next());
}

Ou, vous pouvez utiliser CopyOnWriteArrayList qui est plus lent pour les écritures mais qui n’a pas ce problème.

59
Peter Lawrey

Cela devrait aller tant que vous n'avez pas besoin de la méthode "remove" pour être atomique.

En d'autres termes, si "faire quelque chose" vérifie que l'élément apparaît plusieurs fois dans la liste, il est possible que le résultat de cette vérification soit erroné au moment où vous atteignez la ligne suivante.

Aussi, assurez-vous de synchroniser sur la liste lors de l'itération:

synchronized(list) {
    for (Object o : list) {}
}

Comme l'a mentionné Peter Lawrey, CopyOnWriteArrayList peut vous faciliter la vie et offrir de meilleures performances dans un environnement hautement concurrentiel.

23
assylias

De Collections#synchronizedList(List) javadoc

Retourne une liste synchronisée (thread-safe) sauvegardée par le .__ spécifié. liste. Afin de garantir un accès série, il est essentiel que all l'accès à la liste de sauvegarde est accompli via la liste renvoyée ... Il est impératif que l'utilisateur se synchronise manuellement sur la liste renvoyée lors de son itération. Ne pas suivre ces conseils peut entraîner un comportement non déterministe.

11
gkuzmin

Vous pouvez avoir 2 problèmes différents avec les listes:
1) Si vous effectuez une modification au sein d'une itération même dans un environnement à thread mono, vous aurez une exception ConcurrentModificationException comme dans l'exemple suivant:

List<String> list = new ArrayList<String>();
for (int i=0;i<5;i++)
   list.add("Hello "+i);

for(String msg:list)
   list.remove(msg);

Donc, pour éviter ce problème, vous pouvez faire:

for(int i=list.size()-1;i>=0;i--)
   list.remove(i);

2) Le deuxième problème pourrait être l’environnement multi-threading. Comme mentionné ci-dessus, vous pouvez utiliser synchronized (list) pour éviter les exceptions.

2
gezdy

Cela donnera un comportement cohérent pour les opérations d'ajout/suppression. Mais tout en itérant, vous devez explicitement synchronisé. Référer ce lien

0
Stauz

Oui, cela fonctionnera bien car vous avez synchronized la liste. Je vous suggère d'utiliser CopyOnWriteArrayList.

CopyOnWriteArrayList<String> cpList=new CopyOnWriteArrayList<String>(new ArrayList<String>());

    void remove(String item)
    {
         do something; (doesn't work on the list)
                 cpList..remove(item);
    }
0
amicngh