Java ConcurrentHashMap在性能方面优于HashMap?
我刚读了《清洁代码》一书,偶然发现了这样一句话: Java年轻时,Doug Lea写了一本开创性的书[8] 用Java编程。除了这本书之外,他还写了几本 线程安全集合,该集合后来成为Java ConcurrentHashMap在性能方面优于HashMap?,java,performance,data-structures,concurrency,hashmap,Java,Performance,Data Structures,Concurrency,Hashmap,我刚读了《清洁代码》一书,偶然发现了这样一句话: Java年轻时,Doug Lea写了一本开创性的书[8] 用Java编程。除了这本书之外,他还写了几本 线程安全集合,该集合后来成为 java.util.concurrent包。该包中的集合是安全的 对于多线程情况,它们的性能很好事实上 ConcurrentHashMap实现的性能优于HashMap 几乎所有的情况。它还允许同时进行并发操作 读取和写入,并且它具有支持公共组合的方法 否则不是线程安全的操作。如果Java5是 部署环境,从 Conc
java.util.concurrent
包。该包中的集合是安全的
对于多线程情况,它们的性能很好事实上
ConcurrentHashMap
实现的性能优于HashMap
几乎所有的情况。它还允许同时进行并发操作
读取和写入,并且它具有支持公共组合的方法
否则不是线程安全的操作。如果Java5是
部署环境,从
ConcurrentHashMap
请注意,在上面的引用中,我使用了“[n]”,其中n是一些数字,以表示作者提供参考的地方,正如您所看到的,他没有提供任何粗体部分的参考
并不是说我不相信这句话,而是我想知道这句话的支持证据。那么,有人知道显示ConcurrentHashMap
和HashMap
性能统计信息的资源吗?或者有人能向我解释为什么ConcurrentHashMap比HashMap快
在休息的时候,我可能会研究ConcurrentHashMap在工作中的实现,但现在我想听听其他SOER的答案 这是一种很难用一种或另一种方式证明的有弹性的陈述。在“几乎所有情况”下,你如何衡量某件事
ConcurrentHashMap
可能比同步的HashMap
更好。争论越多,差别就越大。另一方面,非同步的HashMap
可能比ConcurrentHashMap
更快,因为后者会带来不必要的锁定开销
我还想看看这句话的来龙去脉,以及这本书的作者提出了什么证据来支持它。还有未说明的假设的证据,即哈希映射的“几乎所有”用例都涉及同步。如果您仅使用单线程哈希映射访问哈希映射,则最快(它不进行任何同步),如果您是从多个线程访问它,那么ConcurrentHashMap比手工粗粒度同步要快。请参见此处进行比较:
道格·李在这些方面非常擅长,因此如果他的
ConcurrentHashMap
曾经比约书亚·布洛赫的HashMap
表现更好,我也不会感到惊讶。然而,从Java7开始,HashMap
的第一位@author也成为了DougLea。显然现在没有理由HashMap
会比它的并发表慢
出于好奇,我做了一些基准测试。我在Java7下运行它。条目越多,性能越接近。最终,ConcurrentHashMap
与HashMap
的比例在3%以内,这是非常显著的。瓶颈实际上是内存访问,俗话说,“内存就是新磁盘(磁盘就是新磁带)”。如果条目在缓存中,两个条目都将是快速的;如果条目不适合缓存,则两者都会变慢。在实际应用程序中,地图不必太大,就可以与其他地图竞争是否驻留在缓存中。如果经常使用地图,它会被缓存;如果不是,它就不会被缓存,这才是真正的决定因素,而不是实现(假设两者都是由同一位专家实现的)
publicstaticvoidmain(字符串[]args)
{
对于(int i=0;iHashMap可能较慢的原因是,它必须检测ConcurrentModification才能知道何时引发异常。ConcurrentHashMap不必检查modCount以知道何时引发异常(但它确实将其用于size()和isEmpty())。获取锁非常快,尤其是在单线程情况下,当您已经持有锁时,但是检查modCount是两次读取,如果不等于HashMap必须支付抛出CoModException的费用,则跳转
我建议您阅读collections类的源代码,这样您就可以知道它们在进行方法调用时所做的工作。在只有字典get/put的完全私有映射的情况下,您通常可以使用精简的HashMap,而无需任何modCount,甚至无需进行大小跟踪,以提高性能。在上下文中,很难说出作者的意思,但也许作者的意思是坚持“同步”所有HashMap方法都是一个坏主意,使用明确设计的支持多线程的东西,比如ConcurrentHashMap,是一个更好的主意。我在干净的代码中找不到这句话,但它是一本大书…@Matthew,它在183页,页面顶部。它是第13章并发性。@Jeff Foster不是真的,它只是说开始使用ConcurrentHashMap而不是HashMap。我用确切的段落更新了我的问题。如果使用Collections.synchronizedMap(HashMap),您将得到一个作为整个实体锁定的映射。ConcurrentHashMap允许一些并发访问(读写),方法是将锁条带化,以便将哈希表划分为独立锁定的域。@Alvin-我对上下文的理解是引用的语句实际上是指多线程应用程序。前一句是赠品。在这种上下文中,我不会不同意。@StephenC前一句说它对于多线程的情况是安全的,而且它们的性能很好,而有争议的一个说几乎所有的情况-所以我怀疑上下文的范围只是多线程应用程序。我想我仍然同意你最初的评论-证据在哪里?但是作者似乎建议使用ConcurrentHa
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.
}