Estoy usando Kotlin y tengo un mutableMap con Key as String, cuando tengo 2 subprocesos que intentan acceder y poner con la misma clave, suceden algunas cosas raras que mi registro muestra que el tamaño del mapa es 2 (no 1), también registro el clave y valor allí, pero solo hay una entrada ...
@Component class CacheImpl { protected val cache: MutableMap<String, String> = HashMap() //same behavior with ConcurrentHashMap fun fetchFromCache(key: String) { log.info("cache size: ${cache.size}") log.info("cache keys: ${cache.keys}") log.info("cache keys count: ${cache.keys.count()}") cache.computeIfAbsent(id) { key -> fetchFromClient(key) } } } fun serviceCall() { // should fetch from client val result1 = cache.fetchFromCache("123") // should fetch from service val result2 = cache.fetchFromCache("123") }Así que tengo 2 subprocesos llamando a serviceCall al mismo tiempo, en el registro encontré
[thread: 2] - cache size: 0 [thread: 4] - cache size: 0 [thread: 2] - cache keys: [] [thread: 4] - cache keys: [] [thread: 2] - cache keys count: 2 [thread: 4] - cache keys count: 2 [thread: 2] - cache size: 2 [thread: 2] - cache keys count: 2 [thread: 2] - cache keys: [123] [thread: 4] - cache size: 2 [thread: 4] - cache keys count: 2 [thread: 4] - cache keys: [123]También probé con LinkedHashMap y es aún más extraño, en LinkedHashMap puedes ver que mi mapa contiene dos claves exactamente iguales.
[thread: 2] - cache size: 0 [thread: 4] - cache size: 0 [thread: 2] - cache keys: [] [thread: 4] - cache keys: [] [thread: 2] - cache keys count: 2 [thread: 4] - cache keys count: 2 [thread: 2] - cache size: 2 [thread: 2] - cache keys count: 2 [thread: 2] - cache keys: [123,123] [thread: 4] - cache size: 2 [thread: 4] - cache keys count: 2 [thread: 4] - cache keys: [123,123]El HashMap no es seguro para subprocesos, por lo que cuando coloca dos subprocesos, sucedió la escena extraña. Le sugiero que use ConcurrentWeakMap . como dice el doctor
Esta es una implementación muy limitada, no adecuada como reemplazo de un mapa genérico. // Tiene get and put sin bloqueo con refrito sincronizado para simplificar (y mejorar el uso de la CPU en contienda)
Pero solo se asegura de que la operación del mapa sea segura para subprocesos, debe hacer que el método sea seguro para subprocesos.
@Synchronized fun fetchFromCache(key: String) { log.info("cache size: ${cache.size}") log.info("cache keys: ${cache.keys}") log.info("cache keys count: ${cache.keys.count()}") cache.computeIfAbsent(id) { key -> fetchFromClient(key) } } Al mismo tiempo, la clave que usa se genera a partir de fetchFromClient, cada vez que devuelve un nuevo objeto. también es la razón. debe usar putIfAbseent , si desea anular el valor que tiene salidas