【问题标题】:Java Map synchronizationJava 地图同步
【发布时间】:2014-01-28 21:03:37
【问题描述】:

我正在做一些我定义了 Collections.synchronizedMap 的事情。

在一个线程A中,它只会获取值,并放入一个更新的值,所以这不是问题。

在另一个线程 B 中,它将遍历条目,然后根据条目的值,进行一些比较,如果匹配某个条件,它将从映射中删除该条目。

我的理解是同步的 Map 会阻止访问。

但是是否有可能线程 B 获得一个条目,然后线程 A 更新该值,然后线程 B 删除该值,因为它匹配某些条件,但它不应该因为值线程 A 更新它。解决此问题的最佳实施是什么?

【问题讨论】:

  • 您可以锁定评论部分并避免问题

标签: java multithreading synchronization synchronized


【解决方案1】:

听起来您只需要同步“获取、检查和删除”部分。例如:

Iterable<String> keys;
synchronized (map) {
    keys = new ArrayList<>(map.keySet());
}
for (String key : keys) {
    synchronized (map) {
        Integer value = map.get(key);
        // The other thread won't be able to update the value at this point
        if (value != null && value < 0) {
            map.remove(key);
        }
    }
}

您的更新线程可能需要做同样的事情 - 从您的描述中不清楚它是否需要“看到”删除。

【讨论】:

    【解决方案2】:

    您还需要同步您正在使用地图的代码块。

    看这里: Java synchronized block vs. Collections.synchronizedMap

    【讨论】:

      【解决方案3】:

      这可以使用ConcurrentHashMap 完全不阻塞地完成,如下所示:

      final ConcurrentHashMap<String, String> map = new ConcurrentHashMap<>(); // creation
      map.put("aKey", "aValue"); // example content
      
      // Thread A:
      map.replace("aKey", "aNewValue"); // atomic replace call
      
      // Thread B:
      for (String key : map.keySet()) {
        do {
          processed = true;
          final String value = map.get(key);
          if ((value != null) && shouldRemove(value)) { // some comparison or check
            if (map.remove("aKey", value) == false) { // is value still the same?
              processed = false; // value has been changed in between, try again
            }
          }
        } while (processed == false);
      }
      

      这是线程安全的,因为线程 B 只删除值(原子地),如果它没有被改变。如果两者之间确实发生了变化,线程 B 将再次尝试并使用新值进行比较。这类似于其他原子类中使用的compareAndSet 的概念。

      如果线程 A 很少更改,线程 B 将执行几乎与非同步映射相同的速度。如果线程 A 做了很多更改,线程 B 中可能会有多次重复调用,具体取决于进行比较需要多长时间以及值更改的频率。但在所有情况下,这都是线程安全的,并且比任何同步实现都更快。

      【讨论】:

        【解决方案4】:

        听起来您正在尝试实现缓存。那里有许多好的并发缓存实现。我相信guava 的实现相当不错。

        【讨论】:

          【解决方案5】:

          首先关于解决方案。我认为最好的解决方案是标准的 ConcurrentHashMap。它具有用于删除的 CAS 操作,如下所示:

          boolean remove(Object key, Object value)
          Removes the entry for a key only if currently mapped to a given value.
          

          希望对你有帮助。

          现在第二件事是关于“我的理解是同步的地图会阻止访问。”我认为这不是完整的理解,请参阅以下同步地图 javadoc 的详细信息:

          * It is imperative that the user manually synchronize on the returned
           * map when iterating over any of its collection views:
           * <pre>
           *  Map m = Collections.synchronizedMap(new HashMap());
           *      ...
           *  Set s = m.keySet();  // Needn't be in synchronized block
           *      ...
           *  synchronized (m) {  // Synchronizing on m, not s!
           *      Iterator i = s.iterator(); // Must be in synchronized block
           *      while (i.hasNext())
           *          foo(i.next());
           *  }
           * </pre>
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2011-10-21
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多