2012-08-22 15 views
5

Tengo un mapa sincronizada (a través de Collections.synchronizedMap()) que es leído y actualizado por hilo de rosca A. B accede al mapa sólo a través de Map.keySet() (sólo lectura).Cómo sincronizar el mapa entre un subproceso de r/w y un subproceso de solo lectura?

¿Cómo debo sincronizar esta? El docs say que keySet() (para Collections.synchronizedMap) "No necesita estar en el bloque sincronizado". Puedo poner el acceso de lectura/escritura del Subproceso A dentro de un bloque sincronizado, pero ¿es necesario?

supongo que me parece extraño a utilizar incluso un mapa sincronizada, o un bloque sincronizado, si Map.keySet no necesita ser sincronizado (de acuerdo con el enlace de documentos arriba) ...

Actualización: Olvidé que la iteración de keySet debe estar sincronizada, incluso aunque la recuperación de keySet no requiera sincronización. No es particularmente emocionante tener keySet sin poder verlo, por lo que el resultado final = se requiere sincronización. Usando un ConcurrentHashMap en su lugar.

+0

¿se trata de java3D? –

+0

@ tuğrulbüyükışık nope. Pregunta general de concurrencia de Java. – ericsoco

Respuesta

2

Para realizar una verdadera lectura/escritura frente de lectura/Sólo bloqueo Map envoltorio, puede echar un vistazo a la envoltura del Collections utiliza para synchronizedMap() y reemplazar todas las declaraciones synchronized con un ReentrantReadWriteLock. Esto es un buen trabajo. En su lugar, debería considerar cambiar a usar un ConcurrentHashMap que haga todas las cosas correctas allí.

En términos de keySet(), que no necesita estar en un bloque synchronized porque ya está siendo synchronized por el Collections.synchronizedMap(). El Javadocs se acaba de señalar que si usted está iterando a través del mapa, es necesario sincronizar en él porque están haciendo múltiples operaciones, pero no es necesario para sincronizar cuando se está recibiendo el keySet() que se envuelve en una clase de la que SynchronizedSet hace su propia sincronización.

Por último, su pregunta parecía estar dando a entender que no es necesario sincronizar en algo si usted está leyendo de ella. Debe recordar que la sincronización no solo protege contra las condiciones de carrera, sino que también garantiza que los datos sean compartidos correctamente por cada uno de los procesadores. Incluso si está accediendo a Map como de solo lectura, aún necesita sincronizar si cualquier otro hilo lo está actualizando.

+1

Creo que sé lo que estás tratando de decir, pero 'keySet()' * no es * una "copia de las claves en el' Map' "(es una vista en vivo), y no debe ser para no tiene que estar en el bloque sincronizado. Si fuera realmente una copia y no la obtuviera dentro del bloque sincronizado, su iteración podría ser sobre datos obsoletos. Para asegurarse de que fuera reciente, debe incluir el método 'keySet()' en la sincronización. –

+0

+1 para la sugerencia de ConcurrentHashMap, lo investigaré. – ericsoco

+0

Sabía que era típicamente @Mark, pero no eché un vistazo al código. Ahora que lo hago, veo que lo envuelve en un 'SynchronizedSet'. Cambiaré mi respuesta. Gracias. – Gray

2

Los documentos que le están diciendo cómo sincronizar correctamente las operaciones de varios pasos que deben ser atómicas, en este caso la iteración en el mapa:

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()); 
} 

Nota cómo el iteración actual debe estar en un sincronizado bloquear. Los documentos solo dicen que no importa si obteniendo el keySet() está en el bloque sincronizado, porque es una vista en vivo del Map. Si las teclas del mapa cambian entre la referencia al conjunto de teclas que se obtiene y el comienzo del bloque sincronizado, el conjunto de claves reflejará esos cambios.

Y, por cierto, los documentos que cita son solo para Map devueltos por Collections.synchronizedMap. La declaración hace no necesariamente se aplica a todos Map s.

+0

Muy buen punto, me perdí que la iteración de keySet debe estar sincronizada. – ericsoco

2

Los documentos son correctos. El mapa devuelto por Collections.synchronizedMap() se ajustará correctamente sincronizado en todas las llamadas enviadas al Map original.Sin embargo, el conjunto de impl devuelto por keySet() no tiene la misma propiedad, por lo que debe asegurarse de que se lea bajo el mismo bloqueo.

Sin esta sincronización, no hay garantía de que el hilo B jamás verá ninguna actualización hecha por A. Tema

Es posible que desee investigar ConcurrentHashMap. Proporciona una semántica útil para este caso de uso. La iteración sobre una vista de colección en CHM (like keySet()) proporciona un comportamiento concurrente útil (iteradores "débilmente consistentes"). Recorrerá todas las claves desde el estado de la colección en la iteración y podrá ver o no los cambios después de que se haya creado el iterador.

Cuestiones relacionadas