我使用的是Kotlin,我有一个用Key作为String的mutableMap,当我有两个线程试图访问并放入同一个键时,发生了一些奇怪的事情,我的日志显示映射大小是2(而不是1(,我也在那里记录键和值,但只有一个条目。。。
@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")
}
所以我有两个线程同时调用serviceCall,在日志中我发现了
[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]
我也尝试过LinkedHashMap,它更奇怪,在LinkedHashMap中,你可以看到我的映射包含两个完全相同的键!
[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]
- Map不应该总是只维护一个唯一的密钥吗
- 为什么我的地图大小与键数不匹配
HashMap不是线程安全的,所以当你放入两个线程时,就会出现奇怪的场景。我建议你使用ConcurrentWeakMap
。正如医生所说的
这是一个非常有限的实现,不适合作为通用映射替换。//它具有免锁的获取和放置功能,并带有同步的rehash,以实现简单性(以及在争用时更好的CPU使用率(
但它只确保映射操作是线程安全的,您必须使方法是线程安全。
@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)
}
}
同时,每次返回新对象时,您使用的密钥都会从fetchFromClient生成。这也是原因。如果要覆盖退出的值,则应使用putIfAbseent