在使用多线程时,我们是否应该始终使用ConcurrentHashMap?

3
如果我有一个哈希表和这个方法:
private Map<String, String> m = new HashMap<>();

private void add(String key, String value) {
    String val = m.get(key);
    if (val == null) {
        m.put(key, value);
    }
}

如果我有两个线程A和B调用相同的键和值的方法,A和B都可能看到该键不在映射中,因此可能同时写入映射。但是,写入顺序(A在B之前还是B在A之前)不应影响结果,因为它们都写入相同的值。但我想知道并发写是否会危险,并且可能导致意外的结果。在这种情况下,我应该使用ConcurrentHashMap。


你那里的代码明显是错的。add() 方法需要同步,而且 m 必须是 final 或者 volatile。使用 ConcurrentHashMap 并不能解决这些问题。 - undefined
最后一个就够了,你需要一个但不需要两个。 - undefined
不,如果你使用final并且将add()方法同步,它会起作用的。然而,在重负载(多线程)下,ConcurrentHashMap可能提供更好的性能。 - undefined
1
如果我使用synchronized,我认为我们甚至不需要final来保证内存可见性。 - undefined
只要你不重新分配地图,这并不会有任何区别,如果这是你的意思的话。无论如何,我建议使用putIfAbsent而不是使用synchronized。 - undefined
显示剩余6条评论
1个回答

0

是的,你应该使用一个内部线程安全的ConcurrentHashMap,并且使用它的m.putIfAbsent(key, value)方法。

m也应该是final的,以避免被重新赋值。


2
m 需要被声明为 final 或者 volatile - undefined
2
@markspace 只需将其声明为 final。声明为 volatile 没有任何意义。 - undefined
2
@markspace如果m被重新赋值,这段代码在99%的情况下都是有意破坏的。在其他情况下,不需要使用volatile,因为无论是使用什么构造来确保与旧地图一起工作的代码和与新地图一起工作的代码之间的逻辑正确性,都足够了。 - undefined
2
@markspace 看起来你假设了一个不恰当(有争议)的对象发布,其中包含 m,否则就没有必要改变 m 的修饰符。但在这种情况下,声明它为 volatile 是无济于事的,因为这只能确保 volatile 写之前的写操作可见性,而不能防止有争议的后续写操作过早地可见。请注意,你可以在构造函数中使用 synchronized(this),但它和 volatile 一样毫无意义,它不能阻止其他线程过早地看到该对象,只有 final 才能做到这一点。 - undefined
2
@markspace,所以你完全不理解JMM,但还是坚持某件事情,因为“那是经典”,然后你把它搞混了。安全的发布模式是将引用构造对象的字段设置为volatile,而不是构造对象的成员字段。这是一个根本的区别。 - undefined
显示剩余8条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接