Java ConcurrentHashMap优于HashMap性能吗?

时间:2011-07-14 10:48:07

标签: java performance data-structures concurrency hashmap

我刚刚阅读了“清洁代码”一书,并且发现了这句话:

  

当Java年轻时,Doug Lea写了这本开创性的书[8] Concurrent   用Java编程。随着他的书,他开发了几本   线程安全的集合,后来成为了JDK的一部分   java.util.concurrent包。该包中的集合是安全的   对于多线程情况而言,它们表现良好。 事实上,   ConcurrentHashMap实现在中的性能优于HashMap   几乎所有情况。它还允许同时并发   读写,它有支持常见复合的方法   否则不是线程安全的操作。如果Java 5是   部署环境,从一开始   ConcurrentHashMap

请注意,在上面的引用中,我使用了“[n]”,其中n是某个数字,表示作者提供引用的地方,正如您所看到的,他没有为粗体部分提供任何参考。 / p>

不是我不相信这个陈述,但我很想知道这个陈述的支持证据。那么,是否有人知道显示ConcurrentHashMapHashMap的效果统计信息的所有资源?或者任何人都可以向我解释为什么ConcurrentHashMap比HashMap更快?

当我休息时,我可能会调查ConcurrentHashMap的工作实现,但是现在我想听听其他SOers的答案。

4 个答案:

答案 0 :(得分:11)

如果您只使用单个线程访问HashMap,HashMap最快(它不进行任何同步),如果您从多个线程访问它,ConcurrentHashMap比手动粗粒度同步更快。请看这里进行一些比较:

http://www.codercorp.com/blog/java/why-concurrenthashmap-is-better-than-hashtable-and-just-as-good-hashmap.html

答案 1 :(得分:10)

Doug Lea非常擅长这些事情,所以如果有一次他的ConcurrentyHashMap比Joshua Bloch的HashMap表现得更好,我也不会感到惊讶。但是从Java 7开始,H​​ashMap的第一个@author也成了Doug Lea。显然现在HashMap没有理由比它的同伴堂兄慢。

出于好奇,我还是做了一些基准测试。我在Java 7下运行它。条目越多,性能越接近。最终ConcurrentHashMap在HashMap的3%范围内,这是非常了不起的。瓶颈实际上是内存访问,俗话说“内存是新磁盘(磁盘是新磁带)”。如果条目在缓存中,则两者都会很快;如果条目不适合缓存,则两者都会很慢。在实际应用程序中,映射不一定要与其他人竞争驻留在缓存中。如果经常使用地图,它会被缓存;如果没有,它不会被缓存,这是真正的决定因素,而不是实现(两者都由同一专家实现)

public static void main(String[] args)
{
    for(int i=0; i<100; i++)
    {
        System.out.println();

        int entries = i*100*1000;
        long t0=test( entries, new FakeMap() );
        long t1=test( entries, new HashMap() );
        long t2=test( entries, new ConcurrentHashMap() );

        long diff = (t2-t1)*100/(t1-t0);
        System.out.printf("entries=%,d time diff= %d%% %n", entries, diff);
    }
}


static long test(int ENTRIES, Map map)
{
    long SEED = 0;
    Random random = new Random(SEED);

    int RW_RATIO = 10;

    long t0 = System.nanoTime();

    for(int i=0; i<ENTRIES; i++)
        map.put( random.nextInt(), random.nextInt() );

    for(int i=0; i<RW_RATIO; i++)
    {
        random.setSeed(SEED);
        for(int j=0; j<ENTRIES; j++)
        {
            map.get( random.nextInt() );
            random.nextInt();
        }
    }
    long t = System.nanoTime()-t0;
    System.out.printf("%,d ns %s %n", t, map.getClass());
    return t;
}


static class FakeMap implements Map
{
    public Object get(Object key)
    {
        return null;  
    }
    public Object put(Object key, Object value)
    {
        return null;  
    }
    // etc. etc.
}

答案 2 :(得分:7)

HashMap可能变慢的原因是因为它必须检测ConcurrentModification以知道何时抛出异常。 ConcurrentHashMap不必检查modCount以知道何时抛出(但它确实将它用于size()和isEmpty())。获取锁是非常快的,特别是在单线程情况下,当你已经持有锁时,但是检查modCount是两次读取和跳转 - 如果不相等HashMap必须支付以抛出CoModException。

我建议您阅读集合类的源代码,以便了解在进行方法调用时他们正在做多少工作。在只有字典获取/放置的完全私有映射的情况下,您通常可以使用剥离的HashMap而不使用任何modCount或甚至大小跟踪来提高性能。

答案 3 :(得分:6)

这是一种难以证明这种或那种方式的橡皮说明。你如何衡量“几乎所有情况”中的某些内容

ConcurrentHashMap可能比同步HashMap更好。争论越多,差异就越显着。另一方面,未同步的HashMap可能比ConcurrentHashMap更快,因为在后一种情况下不必要的锁定开销。


我也希望看到该陈述的背景,以及该书作者提出的支持它的证据。对于散列映射的“几乎所有”用例的未说明假设的证据涉及同步。