【问题标题】:Using a thread safe static mutable Map of Map in Java在 Java 中使用 Map 的线程安全静态可变 Map
【发布时间】:2022-01-21 16:51:07
【问题描述】:

我需要添加一个静态线程安全的 HashMap。我有这样的东西-

private static Map<String, ConcurrentHashMap<Integer, ClassA>> myCache =
      new ConcurrentHashMap<String, ConcurrentHashMap<Integer, ClassA>>();

即使我使用 ConcurrentHashMap,我看到如果主线程正在 myCache 中添加元素,并且即使稍后在其他线程中访问相同的元素,它也没有最新的myCache 对象中的数据。例如

线程 1:向地图添加条目 myCache = {a1={1=com.x.y.z.ClassA@3ec8b657}}

线程 2:访问相同的密钥 a1。但它没有看到线程 1 添加的数据。而是看到此键 myCache = {a1={}} 的空值 结果,数据被破坏。为线程 1 中的 a1 键添加的条目在线程 2 中不可见。

提前感谢有关如何以线程安全方式更新此映射的任何指示。

【问题讨论】:

  • 请出示minimal reproducible example,说明您当前如何更新和阅读地图。
  • @Ryan 的答案是实现您想要的目标的好方法。请注意,您实现的类不一定扩展 Map。新类必须代理您希望使用的所有 Map 方法(putAll、clear 等)。

标签: java multithreading concurrenthashmap


【解决方案1】:

即使我使用的是 ConcurrentHashMap,我看到如果主线程正在 myCache 中添加元素,并且即使稍后在其他线程中访问相同的元素,它在 myCache 对象中也没有最新数据。

ConcurrentHashMap 在此实例中运行在世界各地的大量应用程序中。如果您相当典型的用例不能正常工作,那么许多关键系统都会出现故障。

有些事情正在发生,但与ConcurrentHashMap 无关的可能性很高。所以这里有一些问题可以帮助你调试你的代码:

  • 您确定缓存查找发生在缓存放置之后吗? ConcurrentHashMap 不会让您摆脱代码中的竞争条件。
  • 这可能是关键对象的hashcode()equals() 函数的问题吗?默认情况下,hashcode()equals() 方法用于对象 instance 而不是对象值。请参阅consistency requirements
  • 缓存查找是否有可能在缓存删除或缓存值超时后发生?有缓存清理逻辑吗?
  • 您是否有在 ConcurrentHashMap 上执行 2 次操作的逻辑。例如测试缓存条目的存在,然后再次调用来放置值?如果是这样,您应该使用 putIfAbsent(...) 或其他原子调用。

如果您编辑帖子并显示带有关键对象的代码示例,则可能会揭示问题的真正根源。

【讨论】:

    【解决方案2】:

    @Ryan 的答案基本上是正确的。

    请记住,您必须代理您希望使用的每个 Map 方法 并且您必须同步到每个代理方法中的cashe 元素。

    例如:

    public void clear()
    {
      synchronized(cache)
      {
        cache.clear();
      }
    }
    

    【讨论】:

      【解决方案3】:

      我在使用 ConcurrentHashMap 时从未有过好的结果。当我需要线程安全时,我通常会这样做:

      public class Cache<K, V> {
      private Map<K, V> cache = new HashMap<>();
      
      public V get(K key) {
          synchronized (cache) {
              return cache.get(key);
          }
      }
      
      public void put(K key, V value) {
          synchronized(cache) {
              cache.put(key, value);
          }
      }
      }
      

      【讨论】:

      • ConcurrentHashMap 是完全并发的。除非您正在拨打多个电话并且不能使用putIfAbsent(...) 或其他电话,否则您不需要对其进行synchronize
      • 非常感谢。这对我有用
      • @Gray 你只是自相矛盾。如果它不能很好地处理跨线程的多个调用,那么它就不是“完全并发的”。正如我之前所说,我从来没有像文档中描述的那样工作过 ConcurrentHashMap。自己处理更容易。
      • 对不起@Ryan,但我没有。 ConcurrentHashMap 永远不会被多次调用破坏。保证它是“完全并发的”。我只是指出,如果您先调用concurrentMap.get(),然后再调用concurrentMap.put(),则地图的状态可能在这两个调用之间发生了变化。这是 your 代码的问题,而不是 ConcurrentHashMap 代码的问题。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2013-02-10
      • 2017-12-26
      • 2014-09-16
      • 2011-08-20
      • 2022-01-22
      • 1970-01-01
      • 2010-10-02
      相关资源
      最近更新 更多