【问题标题】:thread safe map operation线程安全映射操作
【发布时间】:2019-07-08 04:34:33
【问题描述】:

我遇到了以下代码,并指出了一些不一致之处 - 用于多线程安全代码。

    Map<String,Map<String,Set<String>> clusters = new HashMap<.........>;
    Map<String,Set<String>> servers = clusters.get(clusterkey);
    if(servers==null){
      synchronized(clusterkey){
       servers = clusters.get(clusterkey);
       if(servers==null){....initialize new hashmap and put...}
      }
    }
    Set<String> users=servers.get(serverkey);
    if(users==null){
      synchronized(serverkey){
       users=servers.get(serverkey);
       if(users==null){ ... initialize new hashset and put...}
      }
    }
    users.add(userid);
  1. 为什么地图会在 clusterkey 上同步——它不应该作为监视器本身在地图上吗?
  2. 最后一个 users.add... 是否也应该同步?
  3. 要以线程安全的方式添加单个用户,这似乎需要很多代码。什么是更智能的实现方式?

【问题讨论】:

  • 在我们知道确切的上下文之前,没有对错之分。您提供的代码 sn-p 不足以告诉我们最初的实现者在想什么。

标签: java hashmap synchronized


【解决方案1】:

这里只是一些观察:

  1. Synchronizing on a String is a very bad idea -> 在 clusterKeyserverKey 上同步可能无法按预期方式工作。
  2. 最好使用ConcurrentHashMaps 和ConcurrentHashSets。

虽然没有更多上下文,但实际上不可能回答这个问题。似乎代码作者希望为每个 clusterKeyserverKey 安全地创建 1 个映射,因此用户只能添加一次。

一种(可能更好)的方法是只在 clusters 映射本身上使用 synchronize,然后您就安全了,因为只有一个线程可以读取和/或写入所述映射。

另一种方法是使用自定义Locks,可能一个用于读取,另一个用于写入,但如果一个线程正在写入Map,而另一个线程正在读取该确切值,这可能会再次导致不一致来自它。

【讨论】:

    【解决方案2】:

    该代码看起来像是 Double checked locking idiom 的未经过深思熟虑的版本,有时用于延迟初始化。阅读提供的链接,了解为什么这是一个非常糟糕的实现。

    给定代码的问题是它间歇性地失败。当有多个线程尝试使用相同的键(或具有相同哈希码的键)在映射上工作时存在竞争条件,这意味着首先创建的映射可能会被第二个哈希映射替换。

    【讨论】:

    • 这并不完全符合仅链接的答案,但仍然可能只是评论。也许从您提供的维基百科页面添加引用和/或代码,也许提出解决方案?
    • OP 希望 cmets 了解代码的作用,而不是让它始终如一地工作的解决方案。但我可以提供更多细节。
    【解决方案3】:

    1 - 同步试图避免两个线程同时在该 Map 中创建一个新条目。第二个必须等待,所以他的(servers==null) 不会也返回true

    2 - users 列表似乎超出范围,但似乎不需要同步。也许程序员知道没有重复的 userId,或者他不关心一次又一次地重置同一个用户。

    3- ConcurrentHashMap 可能吗?

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-03-14
      • 2020-08-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多