【问题标题】:Java: is using synchronized(this) an advisable practice when creating a ConcurrentHashMap object?Java:在创建 ConcurrentHashMap 对象时使用 synchronized(this) 是一种可取的做法吗?
【发布时间】:2013-01-13 17:57:47
【问题描述】:

我刚刚为我正在参加的分布式编程课程开发了一个 Java Web 服务服务器。其中一项要求是保证我们项目的多线程安全,因此我决定使用 ConcurrentHashMap 对象来存储我的数据。 最后,我留下了一个关于这个 sn-p 代码的问题:

    public List<THost> getHList() throws ClusterUnavailable_Exception{

    logger.entering(logger.getName(), "getHList");
    if(hMap==null){
        synchronized(this){
            if(hMap==null){
                hMap=createHMap();
            }
        }
    }
    if(hMap==null){
        ClusterUnavailable cu = new ClusterUnavailable();
        cu.setMessage("Data unavailable.");
        ClusterUnavailable_Exception exc = new ClusterUnavailable_Exception("Data unavailable.", new ClusterUnavailable());
        throw exc;
    }
    else{
        List<THost> hList = new ArrayList<THost>(hMap.values());
        logger.info("Returning list of hosts. Number of hosts returned = "+hList.size());
        logger.exiting(logger.getName(), "getHList");
        return hList;
    }
}

在创建concurrenthashmap对象本身时是否必须使用synchronized语句,以保证服务在多线程环境中不会出现任何不可预知的行为?

【问题讨论】:

  • 为什么要等到现在才构造hMap?为什么不在构建时初始化它?
  • 这个问题适用于创建任何对象,而不仅仅是 ConcurrentHashMaps。
  • 这是我在同步数组列表的构造时使用的模式。但是,在这种特殊情况下,我希望在客户端请求我的 Web 服务提供的操作之一时创建 hMap。事实上,我实际上在内部 if 中调用 hMap=createHMap(); 并在内部进行一些解组。
  • 虽然在某些情况下您可能希望延迟施工,但这不是其中之一。创建一个空的 HashMap 几乎不使用 CPU 时间或内存。我非常同意路易斯的观点,只需在施工时完成并最终完成。如果您正在构建某个远程服务器的很少使用的文件目录树,那么延迟...

标签: java multithreading web-services synchronized concurrenthashmap


【解决方案1】:

简单的解决方案是通过热切初始化来避免该问题。除非您有明确的证据(即分析)证明急切初始化是一个性能问题,否则这也是最好的解决方案。

至于您的问题,答案是synchronized 块是正确性所必需的。没有它,您可以获得以下事件序列。

  • 线程 1 调用getHList()
  • 线程 1 发现 hMapnull 并开始创建地图。
  • 线程 2 调用 getHList()
  • 线程 2 发现 hMapnull 并开始创建地图。
  • 线程 1 完成创建,并将新映射分配给 hMap,并返回该映射。
  • 线程 2 完成创建,并将 second 新映射分配给 hMap,并返回该映射。

简而言之,如果线程 1 和线程 2 同时调用 getHList()hMap 具有其初始 null 值,则线程 1 和线程 2 可以获得不同的映射。


(在上面,我假设getHList()hMap 的getter。但是,编写的方法不会编译,并且它声明的返回类型与hMap 的类型不匹配...因此不清楚 它的真正意图是什么。)

【讨论】:

  • 当我开始认为同步块是必要的时,这正是我想到的场景。该方法调用一个函数,该函数从 XML 读取数据并将其存储到所述并发散列映射中,然后从包含在并发散列映射中的值构造一个列表并返回所述列表。我很抱歉截断了这个方法,它可能很不清楚。
【解决方案2】:

双重检查锁定模式在 Java 1.5 之前被破坏(在 Java 1.6 及更高版本中效率低下)。见:http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html

考虑使用按需初始化持有者或单元素枚举类型。

【讨论】:

  • 根据那篇文章,它在 Java 1.4 及更早版本而不是 1.5 及更早版本中被破坏。从 1.5 开始,您可以使用 volatile(海报使用的内容)使其工作。
【解决方案3】:

不要打扰。急切地初始化Map,使字段为final,然后放弃同步,直到您证明它确实是必要的。成本微乎其微,而且“明显安全且正确”的解决方案几乎不会太慢。

您提到这是一个课堂项目——专注于让代码正常工作。并发性已经够难了,而且不会增加你必须克服的额外障碍。

【讨论】:

  • 我深信不疑,我会更改代码,以便在构建时初始化地图。
【解决方案4】:

下面这行与 ConcurrentHashMap 无关。它只是创建一个 ConcurrentHashMap 对象的实例。 就像在 JAVA 中同步任何对象创建一样。

hMap=new ConcurrentHashMap&lt;BigInteger, THost&gt;();

【讨论】:

  • 所以我从你的回答中假设确实建议使用synchronized(this)
  • 这取决于。如果你想让你的对象创建同步,答案是肯定的。
  • @wallen - 更好的办法是完全避免这个问题 - 请参阅 Steven Schlansker 的回答。
猜你喜欢
  • 1970-01-01
  • 2015-06-06
  • 2012-02-26
  • 2016-11-23
  • 1970-01-01
  • 1970-01-01
  • 2019-09-09
  • 1970-01-01
相关资源
最近更新 更多