java - threadlocalrandom - ¿Explicar la sincronización de colecciones cuando se usan iteradores?
thread pool java (1)
La iteración sobre colecciones en Java no es segura para subprocesos, incluso si está utilizando una de las envolturas sincronizadas ( Collections.synchronizedMap(...)
):
Es imperativo que el usuario se sincronice manualmente en el mapa devuelto al iterar sobre cualquiera de sus vistas de recopilación:
Map m = Collections.synchronizedMap(new HashMap()); ... Set s = m.keySet(); // Needn''t be in synchronized block ... synchronized(m) { // Synchronizing on m, not s! Iterator i = s.iterator(); // Must be in synchronized block while (i.hasNext()) foo(i.next()); }
Documentos de Java Collection Framework
Otras llamadas a colecciones sincronizadas son seguras, ya que las clases contenedoras las rodean con bloques synchronized
, que usan la colección contenedora como su monitor:
public int size() {
synchronized( this ) {
return collection.size();
}
}
siendo la colección la colección original. Esto funciona para todos los métodos expuestos por una colección / mapa, a excepción de las cosas de iteración.
El conjunto de teclas de un mapa se sincroniza de la misma manera: el contenedor sincronizado no devuelve en absoluto el conjunto de claves original. En cambio, devuelve un contenedor sincronizado especial del conjunto de claves original de la colección. Lo mismo se aplica al conjunto de entrada y al conjunto de valores.
Entiendo que las colecciones como Hashtable están sincronizadas, pero ¿alguien me puede explicar cómo funciona y en qué punto (s) el acceso está restringido a llamadas simultáneas? Por ejemplo, digamos que uso algunos iteradores como este:
Hashtable<Integer,Integer> map = new Hashtable<Integer,Integer>();
void dosomething1(){
for (Iterator<Map.Entry<Integer,Integer>> i = map.entrySet().iterator(); i.hasNext();){
// do something
}
}
void dosomething2(){
for (Iterator<Map.Entry<Integer,Integer>> i = map.entrySet().iterator(); i.hasNext();){
// do something
// and remove it
i.remove();
}
}
void putsomething(int a, int b){
map.put(a,b);
}
void removesomething(int a){
map.remove(a);
}
var clear(){
map = new Hashtable<Integer,Integer>();
}
¿Puede alguien explicar por favor si hay algún problema conmigo llamando a estas funciones al azar desde diferentes hilos? ¿Cómo hace el iterador, en particular, su sincronización, especialmente cuando está utilizando entrySet (), que también parece requerir sincronización? ¿Qué ocurre si se invoca clear () mientras uno de los bucles está en curso? ¿Qué ocurre si removevesomething () elimina un elemento que todavía no se procesa mediante un bucle simultáneo en dosomething1 ()?
¡Gracias por cualquier ayuda!