List<String> list = Collections.synchronizedList(new ArrayList<String>());
synchronized (list) {
list.add("message");
}
Le bloc "synchronisé (liste) {}" est-il vraiment nécessaire ici?
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()); }
Cela dépend du contenu exact du bloc synchronized
:
Si le bloc effectue une seule opération atomique sur la liste (comme dans votre exemple), la variable synchronized
est superflue.
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.
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.
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é.
Lire ceci Oracle Doc
"Il est impératif que l'utilisateur se synchronise manuellement sur la liste renvoyée lors d'une itération"
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
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é.