Java ConcurrentHashMap 最佳实践

相对于HashMap,ConcurrentHashMap提供了内部实现的并发支持。使得开发者在多线程应用中访问ConcurrentHashMap时,不必使用synchronized同步代码块。

 

Java代码   收藏代码
  1. //Initialize ConcurrentHashMap instance  
  2. ConcurrentHashMap<String, Integer> m = new ConcurrentHashMap<String, Integer>();  
  3.    
  4. //Print all values stored in ConcurrentHashMap instance  
  5. for each (Entry<String, Integer> e : m.entrySet())  
  6. {  
  7. system.out.println(e.getKey()+"="+e.getValue());  
  8. }  

上述代码是在多线程应用中创建并使用ConcurrentHashMap的“理论上可行”的示例。这里的“理论上可行”是指,上述代码虽然能够保证 线程安全,但是还是会降低程序性能。但ConcurentHashMap是为了保证线程安全(优于HashMap)的同时改善性能(优于 Hashtable),不是吗?

 

哪里有问题呢?

 

为了搞清楚,我们需要理解ConcurrentHashMap类的内部工作原理。我们最好是从构造函数参数开始。 ConcurrentHashMap完整的构造函数需要三个参数:initialCapacity(初始容量),loadFactory(加载因 子),concurrencyLevel(并发级别)。

  1. initialCapacity:初始容量。ConcurrentHashMap的实现基于加载因子,进行内部分配以容纳这么多元素。
  2. loadFactor:加载因子(表密度),用于建立初始表的大小
  3. concurrencyLevel:并发级别,表示预计的同步更新线程的数量。

前两个参数比较容易理解;并发级别表示分片(shard)的数量,用于在ConcurrentHashMap内部分为相应的分区,同时相同数量的线程被创建,用于在分片级别保证线程安全。

 


 

concurrencyLevel的默认值为16。这意味着我们只要使用默认构造函数创建一个ConcurrentHashMap时,就会创建16 个分片——在我们向map中加入任何键值对之前。它同时意味着各种内部类的实例被创建,如ConcurrentHashMap$Segment, ConcurrentHashMap$HashEntry[] 和ReentrantLock$NofairSync。

 

多数情况下,一个分片已经足够处理通常键值对数量的多线程,同时性能也会被优化。创建多个分片只会使得内部实现更加复杂,同时引入许多不必要的对象,这一切都不利于改善性能。

 

每个使用默认构造函数创建的concurrent hashmap,创建冗余对象的比例约为1到50。例如,没创建100个ConcurrentHashMap实例,将会创建5000个冗余对象。

 

基于以上讨论,一个建议是更明智地使用构造函数参数,以减少冗余对象,同时提高性能。

 

ConcurrentHashMap更好的初始化方式:

Java代码   收藏代码
  1. ConcurrentHashMap<String, Integer> instance = new ConcurrentHashMap<String, Integer>(160.9f, 1);  

初始容量16能够在扩容发生之前容纳足够多的元素。加载因子0.9保证了ConcurrentHashMap内部的致密堆积,以优化内存使用。并发级别设置为1,使得只有一个分片被创建和维护。

 

请注意,如果你的高并发应用程序更新ConcurrentHashMap的频率很高,你应当考虑增大concurrencyLevel,具体数值应该进行严谨的计算、测试以评估。

 

译者注:JDK1.8起通过默认构造函数创建的ConcurrentHashMap,其concurrencyLevel已被设置为1。

Java代码   收藏代码
  1. /** 
  2.  * Creates a new, empty map with an initial table size based on 
  3.  * the given number of elements ({@code initialCapacity}) and 
  4.  * initial table density ({@code loadFactor}). 
  5.  * 
  6.  * @param initialCapacity the initial capacity. The implementation 
  7.  * performs internal sizing to accommodate this many elements, 
  8.  * given the specified load factor. 
  9.  * @param loadFactor the load factor (table density) for 
  10.  * establishing the initial table size 
  11.  * @throws IllegalArgumentException if the initial capacity of 
  12.  * elements is negative or the load factor is nonpositive 
  13.  * 
  14.  * @since 1.6 
  15.  */  
  16. public ConcurrentHashMap(int initialCapacity, float loadFactor) {  
  17.     this(initialCapacity, loadFactor, 1);  
  18. }  

原文写于JDK 1.8发布之前,可以作为JDK 1.8如此优化的解释。

原文链接:Java ConcurrentHashMap Best Practices 

 

又及:

ConcurrentHashMap源码值得再读。相关文章:

  1. 探索 ConcurrentHashMap 高并发性的实现机制 
  2. 深入分析ConcurrentHashMap 
  3. Java集合---ConcurrentHashMap原理分析

http://cwind.iteye.com/blog/2280910

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值