java concurrency java.util.concurrent

java - ¿Necesitamos hacer que ConcurrentHashMap sea volátil?



concurrency java.util.concurrent (4)

Aquí hay 2 subpreguntas : visability de referencia a un mapa y visability de valores escritos en el mapa.

  1. Visibilidad de la referencia al mapa:

    Necesitamos hacer el mapa ...

Debería preocuparse por la publicación segura de sus referencias en entornos de subprocesos múltiples. La publicación segura significa que todos los valores escritos antes de la publicación son visibles para todos los lectores que observan el objeto publicado. Existen las siguientes formas de publicar una referencia de forma segura según JMM:
  1. Proporcionar acceso a la referencia a través de un campo bloqueado correctamente (JLS 17.4.5)
  2. Use el inicializador estático para hacer las tiendas de inicialización (JLS 12.4) ( no es nuestro caso en realidad )
  3. Proporcione acceso a la referencia a través de un campo volátil (JLS 17.4.5), o como consecuencia de esta regla, a través de las clases AtomicX como AtomicReference
  4. Inicialice el valor como campo final (JLS 17.5)

Entonces, en su caso, su referencia de "mapa" no se ha publicado correctamente. Esto puede causar NullPointerException en Test.read () y / o Test.write () (depende de qué hilo crea una instancia de ConcurrentHashMap y lo coloca en el campo "mapa"). El código correcto sería uno de los siguientes:

//1. Provide access to the reference through a properly locked field class Test { ConcurrentHashMap map; synchronized void init(ConcurrentHashMap map) { this.map = map; } synchronized void read() { map.get(object); } synchronized void write() { map.put(key, object); } } // or class Test { ReadWriteLock rwl = new ReentrantReadWriteLock(); ConcurrentHashMap map; void init(ConcurrentHashMap map) { rwl.writeLock().lock(); this.map = map; rwl.writeLock().release(); } void read() { rwl.readLock().lock(); try { map.get(object); } finally { rwl.readLock().release(); } } void write() { rwl.writeLock().lock(); try { map.put(key, object); } finally { rwl.writeLock().release(); } } } // 3. Provide access to the reference via a volatile field class Test { volatile ConcurrentHashMap map; // or AtomicReference<ConcurrentHashMap> map = new AtomicReference(); void init(ConcurrentHashMap map) { this.map = map; } void read() { map.get(object); } void write() { map.put(key, object); } } // 4. Initialize the value as a final field class Test { final ConcurrentHashMap map; Test(ConcurrentHashMap map) { this.map = map; } void read() { map.get(object); } void write() { map.put(key, object); } }

Por supuesto, puede utilizar HashMap simple en el caso de p.1 (cuando trabaja con el "mapa" de campo bloqueado correctamente) en lugar de ConcurrentHashMap. Pero si aún desea utilizar ConcurrentHashMap para un mejor rendimiento, la mejor manera de publicar su "mapa" correctamente, como puede ver, es hacer que el campo sea definitivo.

Aquí hay un buen artículo sobre la publicación segura de un tipo de Oracle, por cierto: http://shipilev.net/blog/2014/safe-public-construction/

  1. Visibilidad de valores escritos en el mapa:

¿Es posible que una inserción en el mapa en un hilo no sea vista o vista muy tarde por un hilo diferente?

No, si no obtiene NPE (vea p.1) o ha publicado su mapa correctamente, un lector siempre verá todos los cambios producidos por un escritor, porque un par de ConcurrentHashMap.put / get produce las barreras de memoria apropiadas / Happens-Before borde.

La misma pregunta para HashMap

HashMap no es seguro para subprocesos. Los métodos HashMap.put / get funcionan con el estado interno del mapa de forma no segura para subprocesos (no atómica, sin visibilidad entre hilos del estado cambiado garantizado), por lo tanto, puede corromper el estado del mapa. Esto significa que debe usar un mecanismo de bloqueo apropiado (secciones sincronizadas, ReadWriteLock, etc.) para trabajar con HashMap. Y, como resultado del bloqueo, logra lo que necesita: un lector siempre ve todos los cambios producidos por un escritor porque esos bloqueos producen barreras de memoria / sucede antes de los bordes.

Tenemos un ConcurrentHashMap compartido que es leído y escrito por 2 hilos.

class Test { ConcurrentHashMap map; read() { map.get(object); } write() { map.put(key, object); } }

¿Necesitamos hacer que el mapa sea volátil para que los hilos del lector vean las escrituras de un hilo lo antes posible?

¿Es posible que una inserción en el mapa en un hilo no sea vista o vista muy tarde por un hilo diferente? La misma pregunta para HashMap.


volatile aplicación volatile ocurre antes de la semántica en lecturas y escrituras en la variable correspondiente.

Un campo puede declararse volatile , en cuyo caso el Modelo de memoria de Java garantiza que todos los subprocesos vean un valor constante para la variable (§17.4).

No tiene nada que ver con los objetos referenciados por el valor de la variable. Entonces, a menos que esté modificando la variable en varios hilos, no, no necesita hacerlo volatile .

Como dice javadoc , todos los métodos ConcurrentHashMap actúan como barreras de memoria,

Las recuperaciones reflejan los resultados de las operaciones de actualización completadas más recientemente que se mantienen desde su inicio. (Más formalmente, una operación de actualización para una clave dada tiene una relación de pase antes de cualquier recuperación (no nula) para esa clave que informa el valor actualizado.


No, tu no.

volatile significa que la variable no se puede almacenar en caché en un registro, por lo que siempre será "escritura" en la memoria. Esto significa que el cambio de un hilo a una variable será visible para otros hilos.

En este caso, la variable es una referencia a un Mapa. Utiliza el mismo mapa todo el tiempo, por lo que no cambia la referencia, sino que cambia el contenido de ese mapa. (Es decir, el mapa es mutable ). Esto también significa que puede, y por lo tanto debería, hacer la referencia al mapa final .

El ConcurrentHashMap difiere del HashMap en que normalmente puede leer con seguridad de él y escribir en él al mismo tiempo desde diferentes subprocesos, sin bloqueo externo. Sin embargo, si desea poder confiar en el tamaño en un punto determinado, realizar operaciones de verificación y escritura o similares, debe diseñarlo usted mismo.


Si puedes hacerlo final , hazlo. Si no puede hacerlo final entonces sí, debería hacerlo volatile . volatile aplica a la asignación de campo y, si no es final , existe la posibilidad (al menos según el JMM) de que una escritura del campo CHM por un hilo no sea visible para otro hilo. Para reiterar, esta es la asignación de campo ConcurrentHashMap y no utiliza el CHM.

Dicho esto, realmente deberías llegar a la final .

¿Necesitamos hacer que el mapa sea volátil para que los hilos del lector vean las escrituras de un hilo lo antes posible?

Si las escrituras de las que habla se hacen utilizando los métodos de mutación del propio CHM (como put o remove ), hacer que el campo sea volátil no tiene ningún efecto. Todas las garantías de visibilidad de la memoria se realizan dentro del CHM.

¿Es posible que una inserción en el mapa en un hilo no sea vista o vista muy tarde por un hilo diferente? La misma pregunta para HashMap.

No para el ConcurrentHashMap. Si está utilizando un HashMap simple al mismo tiempo, no lo haga. Ver: http://mailinator.blogspot.com/2009/06/beautiful-race-condition.html