java - synchronizedmap - ConcurrentHashMap put vs putIfAbsent
thread safe hashmap (2)
Java Docs dice que, putIfAbsent
es equivalente a
if (!map.containsKey(key))
return map.put(key, value);
else
return map.get(key);
Entonces, si la clave existe en el mapa, no actualiza su valor. ¿Es esto correcto?
¿Qué sucede si deseo actualizar un valor de clave basado en algunos criterios? Decir el tiempo de expiración, etc.
¿Sería esto una mejor opción para agregar y actualizar el caché?
public void AddToCache(T key, V value)
{
V local = _cache.putifabsent(key, value);
if(local.equals(value) && local.IsExpired() == false){
return;
}
// this is for updating the cache with a new value
_cache.put(key, value);
}
Por lo tanto, no actualiza el valor de una clave. ¿es esto correcto?
Eso es correcto. Devolverá el valor actual que ya estaba en el mapa.
¿Sería esto una mejor opción para agregar y actualizar el caché?
Un par de cosas mejorarían su implementación.
1. No debe usar putIfAbsent para probar si existe, solo debe usarlo cuando desee asegurarse de que no exista y luego putIfAbsent
. En su lugar, debe usar map.get
para probar su existencia (o map.contains).
V local = _cache.get(key);
if (local.equals(value) && !local.IsExpired()) {
return;
}
2. En lugar de put, querrás reemplazar, esto es porque una condición de carrera puede ocurrir donde el if
puede ser evaluado como falso por dos o más hilos en los que uno de los dos (o más) hilos sobrescribirá las puts del otro hilo.
Lo que puedes hacer es reemplazar
Cuando todo está dicho y hecho, podría verse así
public void AddToCache(T key, V value) {
for (;;) {
V local = _cache.get(key);
if(local == null){
local = _cache.putIfAbsent(key, value);
if(local == null)
return;
}
if (local.equals(value) && !local.IsExpired()) {
return;
}
if (_cache.replace(key, local, value))
return;
}
}
Su código arrojará un NPE si la clave no estaba previamente en el mapa.
Aparte de eso, aunque esta es una idea razonable, no funcionará en un entorno " concurrente ". La razón por la que se agregó el método putIfAbsent()
fue para que el mapa pudiera administrar la atomicidad de la operación utilizando cualquier soporte subyacente que esté utilizando para hacer que las operaciones sean seguras para la ejecución de subprocesos. En su implementación, 2 personas que llaman podrían terminar pisándose el uno al otro (el primero reemplaza un valor caducado con uno nuevo, y el segundo reemplaza inmediatamente el primero nuevo por uno nuevo).