java concurrency hashmap concurrenthashmap double-checked-locking

java - Bloqueo comprobado doble con HashMap regular



concurrency concurrenthashmap (1)

Esta pregunta se confunde en muchos aspectos que es difícil de responder.

Si este código solo se llama desde un solo hilo, entonces lo está haciendo demasiado complicado; no necesitas ninguna sincronización. Pero claramente esa no es tu intención.

Por lo tanto, varios hilos llamarán al método fetch, que delega a HashMap.get () sin ninguna sincronización. HashMap no es seguro para subprocesos. Bam, fin de la historia. Ni siquiera importa si estás tratando de simular el bloqueo con doble verificación; la realidad es que al llamar a get() y put() en un mapa se manipularán las estructuras de datos mutables internas de HashMap , sin una sincronización constante en todas las rutas de código, y dado que puedes llamarlas simultáneamente desde múltiples hilos, ya estás muerto.

(Además, probablemente piense que HashMap.get() es una operación de lectura pura, pero eso también está mal. ¿Qué pasa si HashMap es realmente un LinkedHashMap (que es una subclase de HashMap)? LinkedHashMap.get () actualizará la orden de acceso , lo que implica escribir en estructuras de datos internas, aquí, al mismo tiempo sin sincronización. Pero incluso si get () no está escribiendo, su código aquí todavía está roto.

Regla de oro: cuando crees que tienes un truco ingenioso que te permite evitar la sincronización, casi seguro que estás equivocado.

Volver a la concurrencia Por ahora, está claro que para que el double checked locking funcione, la variable debe declararse como volatile . Pero entonces, ¿qué pasa si el bloqueo verificado doble se utiliza como a continuación.

class Test<A, B> { private final Map<A, B> map = new HashMap<>(); public B fetch(A key, Function<A, B> loader) { B value = map.get(key); if (value == null) { synchronized (this) { value = map.get(key); if (value == null) { value = loader.apply(key); map.put(key, value); } } } return value; } }

¿Por qué tiene que ser realmente un ConcurrentHashMap y no un HashMap regular? Toda la modificación del mapa se realiza dentro del bloque synchronized y el código no utiliza iteradores, por lo que técnicamente no debería haber problemas de "modificación concurrente".

Por favor, evite sugerir el uso de putIfAbsent / computeIfAbsent ya que estoy preguntando sobre el concepto y no el uso de API :) a menos que el uso de esta API contribuya al tema HashMap vs ConcurrentHashMap .

Actualización 2016-12-30

Esta pregunta fue respondida por un comentario a continuación por Holger " HashMap.get no modifica la estructura, pero su invocación de put . Como hay una invocación de get fuera del bloque sincronizado, puede ver un estado incompleto de un put operación que ocurre al mismo tiempo ". ¡Gracias!