1

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

    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对象本身时使用同步语句,以保证服务在多线程环境中不会出现任何不可预知的行为?

4

4 回答 4

7

不要打扰。急切地初始化Map,使字段最终,并删除同步,直到您证明它确实是必要的。成本微乎其微,“显然安全且正确”的解决方案几乎永远不会太慢。

你提到这是一个课堂项目——专注于让代码工作。并发性已经够难了,而无需发明额外的障碍,然后您必须克服这些障碍。

于 2013-01-30T02:26:16.323 回答
5

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

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

  • 线程 1 调用getHList()
  • 线程 1 看到它hMapnull开始创建地图。
  • 线程 2 调用getHList()
  • 线程 2 看到它hMapnull开始创建地图。
  • 线程 1 完成创建,并将新映射分配给hMap,并返回该映射。
  • 线程 2 完成创建,并将第二个新映射分配给hMap,并返回该映射。

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


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

于 2013-01-30T02:48:31.737 回答
1

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

hMap=new ConcurrentHashMap<BigInteger, THost>();

于 2013-01-30T01:37:04.937 回答
0

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

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

于 2013-01-30T01:46:56.567 回答