【问题标题】:Should we always use a ConcurrentHashMap when using multiple threads?使用多线程时我们是否应该始终使用 ConcurrentHashMap?
【发布时间】:2021-03-01 18:36:30
【问题描述】:

如果我有一个哈希映射和这个方法:

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 需要为finalvolatileConcurrentHashMap 不会解决这些问题。
  • 只是在我的情况下,他们总是为每个键写入相同的值,所以我认为同步没有必要。关于 final 和 volatile 的好点,但我看到了这个,也许 final 就足够了,我们不需要 volatile? stackoverflow.com/questions/29404851/…
  • final 就够了,你需要一个但不能同时两个。
  • 如果你使用putIfAbsent,则不需要synchronized
  • 如果我使用 synchronized 我想我们甚至不需要 final 来实现内存可见性?

标签: java concurrency


【解决方案1】:

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

m 也应该是final,以避免它被重新分配。

【讨论】:

  • m 也必须是 finalvolatile
  • @markspace 仅限 final。声明它volatile 是没有意义的。
  • @markspace 如果m 被重新分配,代码在99% 的情况下被设计破坏。在其他情况下,不需要volatile,因为使用了任何构造来确保使用旧地图的代码和使用新地图的代码之间的逻辑正确性就足够了。
  • @markspace 似乎您假设包含m 的对象的发布不正确(活泼),否则,无需更改m 的修饰符。但在这种情况下,将其声明为volatile 将无济于事,因为这只会确保在volatile 写入之前写入的可见性,但不会阻止后续写入的过早可见性。请注意,您可以在构造函数中使用synchronized(this),但它与volatile 一样毫无意义,它不能阻止其他线程过早看到该对象,只有final 可以做到这一点.
  • @markspace 所以你根本不了解 JMM,但一直坚持某件事,因为“那是经典”,然后你就把它搞混了。安全发布模式是让字段引用构造对象volatile,而不是构造对象的成员字段。这是根本的区别。
猜你喜欢
  • 2021-10-03
  • 1970-01-01
  • 2016-12-05
  • 1970-01-01
  • 1970-01-01
  • 2016-02-16
  • 2012-06-17
  • 2020-03-05
  • 1970-01-01
相关资源
最近更新 更多