Java ConcurrentHashMap在性能方面优于HashMap?

我刚刚读了《清洁代码》这本书,并发现了这句话:

当Java年轻时,Doug Lea写了一本开创性的书[8]Java中的并发编程。除了这本书,他还开发了几个线程安全集合,后来成为该软件包中JDK的一部分。该包中的集合对于多线程情况是安全的,并且它们性能良好。事实上,ConcurrentHashMap实现在几乎所有情况下都比HashMap表现得更好。它还允许同时进行并发读取和写入,并且它具有支持常见复合操作的方法,否则这些操作不是线程安全的。如果 Java 5 是部署环境,请从java.util.concurrentConcurrentHashMap

请注意,在上面的引文中,我使用了“[n]”,其中n是某个数字,以指示作者提供参考的地方,并且如您所见,他没有为粗体部分提供任何参考。

并不是说我不相信这种说法,但我很想知道这种说法的支持证据。那么,有没有人知道任何显示和的性能统计信息的资源?或者任何人都可以向我解释为什么 ConcurrentHashMap 比 HashMap 快?ConcurrentHashMapHashMap

当我休息一下时,我可能会研究ConcurrentHashMap在工作中的实现,但现在我想听听其他SOers的答案。


答案 1

道格·李(Doug Lea)非常擅长这些事情,所以如果他一度比约书亚·布洛赫(Joshua Bloch)的表现更好,我不会感到惊讶。然而,从Java 7开始,第一个@author也变成了Doug Lea。显然,现在没有理由比它的并发表亲慢。ConcurrentHashMapHashMapHashMapHashMap

出于好奇,我还是做了一些基准测试。我在Java 7下运行它。参赛作品越多,性能越接近。最终在3%以内,这是非常了不起的。瓶颈实际上是内存访问,俗话说,“内存是新磁盘(磁盘是新磁带)”。如果条目在缓存中,则两者都将很快;如果条目不适合缓存,则两者都会很慢。在实际应用程序中,地图不必很大就可以与其他地图竞争驻留在缓存中。如果地图经常使用,则会对其进行缓存;如果不是,则不会缓存它,这是真正的决定因素,而不是实现(假设两者都由同一个专家实现)ConcurrentHashMapHashMap

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

如果您仅使用单个线程访问HashMap,则HashMap速度最快(它不执行任何同步),如果您从多个线程访问它,则ProcurrentHashMap比手动进行粗粒度同步更快。有关一些比较,请参阅此处:

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


推荐