MutableMap 被多线程修改且 map.size 不等于键数

发布于 2025-01-13 08:24:51 字数 1692 浏览 1 评论 0原文

我正在使用 Kotlin,并且我有一个 key 作为 String 的 mutableMap,当我有 2 个线程尝试访问并使用相同的 key 放置时,会发生一些奇怪的事情,我的日志显示映射大小为 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")
}

所以我有2个线程同时调用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 不应该只维护一个唯一的键吗?
  • 为什么我的地图大小与按键数量不匹配?

I am using Kotlin and I have a mutableMap with Key as String, when I have 2 threads trying to access and put with same key, some weird things happen that my log shows the map size is 2 (not 1), I also log the key and value there but there is only one entry...

@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")
}

So I have 2 threads calling the serviceCall at same time, in log I found

[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]

I also tried with LinkedHashMap and it's even more weird, in LinkedHashMap you can see my map contains two exact same key!

[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]
  • shouldn't Map only maintain a unique key always?
  • Why my map size is not matching with key count?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

滥情空心 2025-01-20 08:24:51

HashMap不是线程安全的,所以当你放入两个线程时,就会发生奇怪的场景。我建议您使用ConcurrentWeakMap。正如医生所说

这是一个非常有限的实现,不适合作为通用地图替代品。
// 为了简单起见,它具有无锁获取和同步重新哈希的放置(以及在争用时更好的 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 生成。这也是原因。如果您想覆盖已退出的值,您应该使用 putIfAbsent

The HashMap is not thread-safe, so when you put in two threads, it happened the weird scene. I suggest you use the ConcurrentWeakMap. as the doc says

This is a very limited implementation, not suitable as a generic map replacement.
// It has lock-free get and put with synchronized rehash for simplicity (and better CPU usage on contention)

But it only makes sure the map operation is thread-safe, you have to make the method thread-safe.


@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)
   }
}

At the same time, the key you use generates from fetchFromClient, every time it returns a new object. it is also the reason. you should use the putIfAbseent, if you want override the value which has exits

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文