如果只有一个writer线程并且没有对映射进行结构修改,那么我们是否需要同步java HashMap get

如果只有一个writer线程并且没有对映射进行结构修改,那么我们是否需要同步java HashMap get,java,multithreading,hashmap,synchronization,Java,Multithreading,Hashmap,Synchronization,在我的应用程序中,我需要在内存中维护一个HashMap,它存储用户ID列表及其分数 有一个Writer线程根据业务逻辑更新用户的分数。许多读者线程从这个地图上读取用户的分数,即map.get(userId) userid的列表是静态的,即没有新用户添加到地图中 根据JavaDoc的说法,如果多个线程同时访问一个哈希映射,并且至少有一个线程在结构上修改了映射,那么它必须在外部同步。 我没有进行任何结构更改(没有添加/删除)。对于这种用例,我是否需要使用ConcurrentHashMap或任何其他同

在我的应用程序中,我需要在内存中维护一个
HashMap
,它存储
用户ID列表及其
分数

有一个Writer线程根据业务逻辑更新用户的分数。许多读者线程从这个地图上读取用户的
分数,即
map.get(userId)

userid的列表是静态的,即没有新用户添加到地图中

根据JavaDoc
的说法,如果多个线程同时访问一个哈希映射,并且至少有一个线程在结构上修改了映射,那么它必须在外部同步。


我没有进行任何结构更改(没有添加/删除)。对于这种用例,我是否需要使用
ConcurrentHashMap
或任何其他
同步
构造

您的
map.put
操作将只更新
HashMap$Node
value
字段。这对于
HashMap
的结构一致性来说是安全的,但是在
value
字段上仍然存在数据竞争。如果您的值类型是一个简单的值类,如
Integer
Long
Double
,即使在数据竞争中也可以安全地取消对它的引用,但不能保证消费者会看到分数更新

一个干净的解决方案是用
AtomicLong
替换您的值类型,例如
Long
。然后你的地图将永远不会被更新,只有它的值会以线程安全的方式被改变

这是解决方案的概要:

  • 安全发布地图:

    volatile Map<Player, AtomicLong> scores;
    
    void publishScores() {
       scores = unmodifiableMap(createScoresMap());
    }
    
  • 读一段乐谱:

    long getScore(Player p) {
       return map.get(p).get();
    }
    

  • 如果没有正确的同步,就不能保证“读取”线程将读取正确的值,但在您的情况下不会发生ConcurrentModificationException。为了研究良好的并发实践,我建议使用以下资源:您可以有一个
    userId->wrapper
    的映射,其中wrapper包含一个
    volatile scrore
    。简单地读写
    score
    就可以了。
    long getScore(Player p) {
       return map.get(p).get();
    }