2009-06-16 20 views
26

Tengo un Hashmap que, por razones de velocidad, me gustaría no requerir el bloqueo. ¿Actualizarlo y acceder a él al mismo tiempo causará algún problema, suponiendo que no me preocupen los datos obsoletos?Problema de concurrencia de Hashmap

Mis accesos se obtienen, no se repiten a través de él, y las eliminaciones son parte de las actualizaciones.

+0

¿Las actualizaciones incluyen las eliminaciones? ¿Sus accesos incluyen iterar sin embargo? –

+0

No importa, creo que esas preguntas son irrelevantes. –

+0

¿O son ellos? [Necesito más sueño] –

Respuesta

50

Sí, causará problemas importantes. Un ejemplo es lo que podría suceder al agregar un valor al mapa hash: esto puede causar una repetición de la tabla, y si eso ocurre mientras otro hilo itera sobre una lista de colisión (una tabla hash "bucket"), ese hilo podría erróneamente no puede encontrar una clave que existe en el mapa. HashMap es explícitamente inseguro para uso concurrente.

Use ConcurrentHashMap en su lugar.

7

Las condiciones que describe no serán satisfechas por HashMap. Como el proceso de actualización de un mapa no es atómico, puede encontrar el mapa en un estado no válido. Múltiples escrituras pueden dejarlo en un estado corrupto. ConcurrentHashMap (1.5 o posterior) hace lo que quiere.

4

Si con 'al mismo tiempo' te refieres a varios hilos, entonces sí debes bloquear el acceso al mismo (O usa ConcurrentHashMap o similar que hace el bloqueo por ti).

0

No, no habrá ningún problema si lo hace lo siguiente:

  1. Coloque sus datos en el HashMap en la primera carga de un solo hilo antes de que ocurra cualquier multihilo. Esto se debe a que el proceso de agregar datos altera el número de cambio y es diferente la primera vez que lo agrega (se devolverá un valor nulo) en lugar de reemplazar los datos (se devolverán los datos anteriores, pero el número de cambio no se modificará). Modcount es lo que hace que los iteradores fallen rápidamente. Sin embargo, si usa get, no se repetirá nada, por lo que está bien.

  2. Tenga las mismas claves en toda la aplicación. Una vez que la aplicación se inicia y carga sus datos, no se pueden asignar otras claves a este mapa. De esta forma, un get obtendrá datos obsoletos o datos que se insertaron frescos; no habrá problemas.

12

En caso de duda, consulte de Javadocs la clase:

Tenga en cuenta que esta aplicación no está sincronizado. Si hay varios subprocesos que acceden simultáneamente a un mapa hash y al menos uno de los subprocesos modifica estructuralmente el mapa, debe estar sincronizado externamente. (Una modificación estructural es cualquier operación que agrega o elimina una o más asignaciones, simplemente cambiar el valor asociado con una clave que ya contiene una instancia no es una modificación estructural). Esto se logra sincronizando un objeto que naturalmente encapsula el mapa . Si no existe tal objeto, el mapa debe ser "ajustado" utilizando el método Collections.synchronizedMap.Esto se hace mejor en tiempo de creación, para evitar el acceso no sincronizado accidentales sufridos por el mapa:

Map m = Collections.synchronizedMap(new HashMap(...));

(énfasis no la mía)

Así que basado en el hecho de que usted ha dicho que sus hilos serán eliminando mapeos del Mapa, la respuesta es que definitivamente causará problema y sí, definitivamente es inseguro.

16

No se puede subestimar la importancia de sincronizar o usar ConcurrentHashMap.

Hasta hace un par de años tenía la impresión errónea de que podía salirse con la suya solo sincronizando las operaciones de poner y quitar en un HashMap. Esto es, por supuesto, muy peligroso y, en realidad, da como resultado un ciclo infinito en HashMap.get() en algunos jdk (creo que a principios de los 1.5).

Lo que hice hace un par de años (y realmente no debe hacerse):

public MyCache { 
    private Map<String,Object> map = new HashMap<String,Object>(); 

    public synchronzied put(String key, Object value){ 
     map.put(key,value); 
    } 

    public Object get(String key){ 
     // can cause in an infinite loop in some JDKs!! 
     return map.get(key); 
    } 
} 

EDITAR: que me gustaría añadir un ejemplo de lo que nohacer (véase más arriba)

+4

Creo que el bucle infinito es una característica de todos los Sun JDK. No puedo recordar cuál, pero un software de código abierto bien conocido estaba usando HashMap para el registro, por lo que no se sincronizó para la velocidad ya que no se requería una precisión completa. En producción, muy ocasionalmente entraría en un ciclo infinito, mucho peor que lanzar una excepción sin control. –

+1

Lo he visto yo mismo. Si intenta actualizar un HashMap en varios subprocesos con un enfoque "no me importa" sobre la corrupción, se bloqueará su JVM. HashMap no sincronizado/no seguro solo es seguro si no tiene actualizaciones/eliminaciones o solo un subproceso tiene acceso a él. –

0

Al igual que otros mencionan el uso de un ConcurrentHashMap o sincronizar el mapa cuando su actualización.

0

He leído aquí o en otro lugar, no, no tiene acceso desde múltiples hilos, pero nadie dice lo que realmente sucede.

Entonces, he visto hoy (es por eso que estoy en esta - vieja) pregunta en una aplicación que se ejecuta en producción desde marzo: 2 ponen el mismo HashSet (entonces HashMap) causan una sobrecarga de la CPU (cerca del 100%) , y el aumento de la memoria de 3 GB, luego baja por GC. Tenemos que reiniciar la aplicación.