web-dev-qa-db-fra.com

Collections.synchronizedList et synchronized

List<String> list = Collections.synchronizedList(new ArrayList<String>());
synchronized (list) {
    list.add("message");
}

Le bloc "synchronisé (liste) {}" est-il vraiment nécessaire ici?

63
romsky

Vous n'avez pas besoin de synchroniser comme vous le mettez dans votre exemple. CEPENDANT, il est très important de synchroniser la liste lorsque vous la parcourez (comme indiqué dans la Javadoc): 

Il est impératif que l'utilisateur se synchronise manuellement sur le .__ retourné. liste quand on itère dessus:

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

Cela dépend du contenu exact du bloc synchronized:

  1. Si le bloc effectue une seule opération atomique sur la liste (comme dans votre exemple), la variable synchronized est superflue.

  2. Si le bloc effectue plusieurs opérations sur la liste - et doit conserver le verrou pendant la durée de l'opération composée -, la variable synchronized est pas superflue. Un exemple courant est celui qui parcourt la liste.

29
NPE

Le code sous-jacent pour la méthode add Collections.synchronizedList est:

public void add(int index, E element) {
    synchronized (mutex) {list.add(index, element);}
}

Ainsi, dans votre exemple, il n'est pas nécessaire d'ajouter une synchronisation.

20
assylias

Il est également important de noter que toute méthode utilisant des itérateurs, par exemple Collections.sort (), devra également être encapsulée dans un bloc synchronisé.

16
jpegjpg

Lire ceci Oracle Doc

"Il est impératif que l'utilisateur se synchronise manuellement sur la liste renvoyée lors d'une itération"

7
snan

Comme ce qui a été mentionné par d'autres, les collections synchronisées sont thread-safe , mais les actions composées de ces collections ne sont pas garanties comme étant thread-safe par défaut. 

Selon JCIP, les actions composées communes peuvent être 

  • itération
  • la navigation
  • put-si-absent
  • check-then-act

Le bloc de code synchronisé de l'OP n'est pas une action composée, il n'y a donc aucune différence entre l'ajouter ou non.

Prenons l'exemple de JCIP et modifions-le un peu pour préciser pourquoi il est nécessaire de protéger les actions composées avec lock.

Deux méthodes fonctionnent sur la même collection list qui est enveloppée par Collections.synchronizedList

public Object getLast(List<String> list){
    int lastIndex = list.size() - 1;
    return list.get(lastIndex);
}

public void deleteLast(List<String> list){
    int lastIndex = list.size() - 1;
    list.remove(lastIndex);
}

Si les méthodes getLast et deleteLast sont appelées en même temps par deux threads différents, des intercalaires en dessous peuvent se produire et getLast lancera ArrayIndexOutOfBoundsException. Supposons que la variable lastIndex courante est 10. 

Fil A (deleteLast) -> supprimer
Sujet B (getLast) --------------------> get 

Le thread A remove l'élément avant l'opération get dans le thread B. Ainsi, le thread B utilise toujours 10 comme méthode lastIndex pour appeler list.get, ce qui entraînera un problème simultané. 

1
Gearon