HashMap坚持下去

时间:2016-07-14 20:38:16

标签: java multithreading

我在多线程环境中编码,我看到线程卡在HashMap.put上:

 34 Threads
 java.util.HashMap.put(HashMap.java:374) 
 com.aaa.bbb.MyClass.getDefinitionMap().

调查HashMap的方法我发现该方法已同步:

@Override
public synchronized Map<String,String> getDefinitionMap() {
        //truncated some code here...
        colDefMap = new HashMap<String,String>();           
        for (CD cd : (List<CD>)cm.getDef()) {
            colDefMap.put(cd.getIdentifier(),cd);
        }
    return colDefMap;
}

因此,切换到ConcurrentHashMap后,从方法签名中删除synchronized关键字并重新启动应用程序服务器 - 问题已解决。

我的问题是为什么在这种情况下同步方法不足以保护地图免受并发访问?

4 个答案:

答案 0 :(得分:2)

您正在同步子类中的getDefinitionMap方法,显然不是唯一可以访问cm的方法(或类)。

类变量cm上的迭代器可能是罪魁祸首:

for (CD cd : (List<CD>) cm.getDef())
{
    colDefMap.put(cd.getIdentifier(), cd);
}

在上面的代码中,cm变量可能在您迭代时被修改。

您可以使用以下内容:

synchronized (cm)
{
    for (CD cd : (List<CD>) cm.getDef())
    {
        colDefMap.put(cd.getIdentifier(), cd);
    }
}

但是,如果在没有类似同步的情况下执行对cm的修改,这仍然会对cm的其他线程进行修改。

正如您所发现的,使用集合类的线程安全版本要比在多线程环境中为非线程安全集合实现变通方法容易得多。

答案 1 :(得分:2)

你不会说这是多么“卡住”,无论你是否真的遇到僵局或瓶颈。

我希望发布的代码成为瓶颈,几乎所有线程都试图访问同一个对象,等待获取synchronized方法使用的锁。无论cm.getDef确实需要一段时间,并且一次只有一个线程可以取得进展。因此,同步确实可以保护数据不受并发访问的影响,只是以吞吐量为代价。

这符合the Java concurrency tutorial中给出的“饥饿”的定义:

  

Starvation描述了一种情况,即线程无法获得对共享资源的定期访问,并且无法取得进展。当“贪婪”线程使共享资源长时间不可用时,就会发生这种情况。例如,假设一个对象提供了一个通常需要很长时间才能返回的同步方法。如果一个线程经常调用此方法,则通常需要对同一对象进行频繁同步访问的其他线程也会被阻塞。

正如您所观察到的,切换到ConcurrentHashMap是一个很好的改进。 ConcurrentHashMap避免将线程锁定在整个地图之外,并支持并发更新,请参阅the API doc(我的重点):

  

支持检索的完全并发和更新的高预期并发性的哈希表。该类遵循与Hashtable相同的功能规范,并包括与Hashtable的每个方法相对应的方法版本。但是,即使所有操作都是线程安全的,检索操作也不需要锁定,并且不支持以阻止所有访问的方式锁定整个表。此类可与之完全互操作在依赖于线程安全但不依赖于其同步细节的程序中的Hashtable。

您可以考虑缓存cm.getDef所做的任何事情,这样您就不必每次都调用它,但其实用性当然取决于您的要求。

答案 2 :(得分:2)

我认为你认为解决问题可能是错的。删除同步意味着您可以解锁对此方法的访问权限,从而解决您的问题并带来其他问题。我的意思是你的HashMap是在你的函数范围内创建的,所以它显然不是你应该有一个并发问题(如果放在里面的东西不是静态的或线程安全的)。使用concurrentHashMap从未如此有效。

我建议你尝试在多线程测试中看看你的函数是否正常工作是没有synchronized语句的作业(没有concurrentMap)。

在我看来,在不知道你的其余代码的情况下,这个函数可能正在访问可能被线程锁定的静态或共享数据,所以问题不是来自函数,而是在某个时刻与其交互的其他对象。

答案 3 :(得分:1)

你在其他地方修改它吗?你是否100%确定它不在其他地方put?我怀疑你是,并且可能是第二次放置导致无限循环。 http://mailinator.blogspot.com/2009/06/beautiful-race-condition.html

否则,如果这是您修改HashMap的唯一地方,那应该没问题。