java - ¿Es necesario envolver un HashMap de Concurrent en un bloque sincronizado?
synchronized concurrenthashmap (2)
La sincronización de esas operaciones no tiene ningún beneficio aquí: en realidad, degrada el rendimiento si no necesita sincronización.
La razón por ConcurrentHashMap
se creó ConcurrentHashMap
, es que los mapas sincronizados (implementados a mano como en la pregunta o instanciados de la forma habitual con Collections.synchronizedMap(map)
) muestran un rendimiento deficiente cuando se accede a muchos subprocesos. Las operaciones de poner y obtener están bloqueando, por lo que todos los demás subprocesos deben esperar y no pueden acceder al mapa al mismo tiempo. El ConcurrentHashMap
, como sugiere su nombre, permite el acceso concurrente por otro lado. Pierde este beneficio si agrega sincronización.
¿Todas las operaciones que no son de recuperación en un Mapa de Hash Concurrente ( put()
, remove()
etc.) deben estar envueltas en un bloque synchronized(this)
? Entiendo que todas estas operaciones son seguras para subprocesos, entonces, ¿hay algún beneficio / necesidad real al hacerlo? Las únicas operaciones utilizadas son put()
y remove()
.
protected final Map<String, String> mapDataStore = new ConcurrentHashMap<String, String>();
public void updateDataStore(final String key, final String value) {
...
synchronized (this) {
mapDataStore.put(key, value);
}
...
}
No, estás perdiendo los beneficios de ConcurrentHashMap
haciendo eso. También puede utilizar un HashMap
con un HashMap
synchronized
o synchronizedMap()
para bloquear toda la tabla (que es lo que hace cuando ajusta las operaciones en modo synchronized
, ya que el monitor implícito es la instancia del objeto completo).
El propósito de ConcurrentHashMap
es aumentar el rendimiento de su código concurrente permitiendo lecturas / escrituras concurrentes en la tabla sin bloquear toda la tabla. La tabla lo admite internamente mediante el uso de franjas de bloqueo (múltiples bloqueos en lugar de uno, con cada bloqueo asignado a un conjunto de cubos de hash; consulte Java Concurrency in Practice por Goetz et al).
Una vez que esté utilizando ConcurrentHashMap
, todos los métodos de mapa estándar ( put()
, remove()
, etc.) se vuelven atómicos en virtud de la separación de bloqueo, etc. en la implementación. Las únicas isEmpty()
y desventajas son que los métodos como size()
y isEmpty()
no necesariamente arrojan resultados precisos, ya que la única forma en que podrían ser las operaciones para bloquear toda la tabla.
La interfaz de la interfaz ConcurrentMap
también agrega nuevas operaciones de compuestos atómicos como putIfAbsent()
(ponga algo solo si la clave no está ya en el mapa), remove()
aceptando tanto la clave como el valor (elimine una entrada solo si su valor es igual a un parámetro que usted pass), etc. Estas operaciones solían requerir el bloqueo de toda la tabla porque necesitaban dos llamadas de método para cumplir (por ejemplo, putIfAbsent()
necesitaría llamadas tanto a containsKey()
como a put()
, envueltas dentro de un bloque synchronized
, si estuviera usando una implementación de Map
estándar.) Una vez más, obtiene un mayor rendimiento utilizando estos métodos, al evitar bloquear toda la tabla.