Collections.synchronizedList y sincronizado

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

¿El bloque “sincronizado (lista) {}” realmente necesita aquí?

No necesita sincronizar como pone en su ejemplo. SIN EMBARGO, muy importante, necesita sincronizar alrededor de la lista cuando la itere (como se indica en el Javadoc):

Es imperativo que el usuario se sincronice manualmente en la lista devuelta al iterar sobre ella:

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

Depende del contenido exacto del bloque synchronized :

  1. Si el bloque realiza una sola operación atómica en la lista (como en su ejemplo), el synchronized es superfluo.

  2. Si el bloque realiza múltiples operaciones en la lista y necesita mantener el locking durante la operación compuesta , entonces el synchronized no es superfluo. Un ejemplo común de esto es iterar sobre la lista.

El código subyacente para el método de agregación Collections.synchronizedList es:

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

Entonces, en su ejemplo, no es necesario agregar sincronización.

También es importante tener en cuenta que cualquier método que use Iterators, por ejemplo, Collections.sort (), también deberá estar encapsulado dentro de un bloque sincronizado.

Lea este documento de Oracle

Dice “Es imperativo que el usuario se sincronice manualmente en la lista devuelta al iterar sobre ella”

Al igual que lo que otros han mencionado, las colecciones sincronizadas son seguras para subprocesos , pero no se garantiza que las acciones compuestas para estas colecciones sean seguras para subprocesos de forma predeterminada.

Según JCIP, las acciones compuestas comunes pueden ser

  • iteración
  • navegación
  • poner-si-ausente
  • verificar-luego-actuar

El bloque de código sincronizado de OP no es una acción compuesta, por lo que no importa si lo agrega o no.

Tomemos el ejemplo de JCIP y modifíquelo un poco para aclarar por qué es necesario proteger las acciones compuestas con el locking.

Hay dos métodos que operan en la misma list colecciones envueltos por Collections.synchronizedList

 public Object getLast(List list){ int lastIndex = list.size() - 1; return list.get(lastIndex); } public void deleteLast(List list){ int lastIndex = list.size() - 1; list.remove(lastIndex); } 

Si los métodos getLast y deleteLast se deleteLast al mismo tiempo mediante dos subprocesos diferentes, a continuación pueden producirse entrelazamientos y getLast lanzará ArrayIndexOutOfBoundsException . Suponer que lastIndex actual es 10.

Subproceso A (eliminar Última) -> eliminar
Thread B (getLast) ——————–> get

El subproceso A remove el elemento antes de la operación get en el subproceso B. Por lo tanto, el subproceso B sigue utilizando 10 como el lastIndex para llamar list.get método list.get , conducirá a un problema concurrente.