ConcurrentHashMap
ConcurrentHashMap是一个线程安全的Hash Table,它的主要功能是提供了一组和HashTable功能相同但是线程安全的方法。ConcurrentHashMap可以做到读取数据不加锁,并且其内部的结构可以让其在进行写操作的时候能够将锁的粒度保持地尽量地小,不用对整个ConcurrentHashMap加锁。
ConcurrentHashMap的内部结构
ConcurrentHashMap为了提高本身的并发能力,在内部采用了一个叫做Segment的结构,一个Segment其实就是一个类Hash Table的结构,Segment内部维护了一个链表数组,我们用下面这一幅图来看下ConcurrentHashMap的内部结构:
从上面的结构我们可以了解到,ConcurrentHashMap定位一个元素的过程需要进行两次Hash操作,第一次Hash定位到Segment,第二次Hash定位到元素所在的链表的头部,因此,这一种结构的带来的副作用是Hash的过程要比普通的HashMap要长,但是带来的好处是写操作的时候可以只对元素所在的Segment进行加锁即可,不会影响到其他的Segment,这样,在最理想的情况下,ConcurrentHashMap可以最高同时支持Segment数量大小的写操作(刚好这些写操作都非常平均地分布在所有的Segment上),所以,通过这一种结构,ConcurrentHashMap的并发能力可以大大的提高。
ConcurrentHashMap的初始化
下面我们来结合源代码来具体分析一下ConcurrentHashMap的实现,先看下初始化方法:
-
publicConcurrentHashMap(intinitialCapacity,
-
floatloadFactor,intconcurrencyLevel){
-
if(!(loadFactor>0)||initialCapacity<0||concurrencyLevel<=0)
-
thrownewIllegalArgumentException();
-
-
if(concurrencyLevel>MAX_SEGMENTS)
-
concurrencyLevel=MAX_SEGMENTS;
-
-
-
intsshift=0;
-
intssize=1;
-
while(ssize<concurrencyLevel){
-
++sshift;
-
ssize<<=1;
-
}
-
segmentShift=32-sshift;
-
segmentMask=ssize-1;
-
this.segments=Segment.newArray(ssize);
-
-
if(initialCapacity>MAXIMUM_CAPACITY)
-
initialCapacity=MAXIMUM_CAPACITY;
-
intc=initialCapacity/ssize;
-
if(c*ssize<initialCapacity)
-
++c;
-
intcap=1;
-
while(cap<c)
-
cap<<=1;
-
-
for(inti=0;i<this.segments.length;++i)
-
this.segments[i]=newSegment<K,V>(cap,loadFactor);
-
}
CurrentHashMap的初始化一共有三个参数,一个initialCapacity,表示初始的容量,一个loadFactor,表示负载参数,最后一个是concurrentLevel,代表ConcurrentHashMap内部的Segment的数量,ConcurrentLevel一经指定,不可改变,后续如果ConcurrentHashMap的元素数量增加导致ConrruentHashMap需要扩容,ConcurrentHashMap不会增加Segment的数量,而只会增加Segment中链表数组的容量大小,这样的好处是扩容过程不需要对整个ConcurrentHashMap做rehash,而只需要对Segment里面的元素做一次rehash就可以了。
整个ConcurrentHashMap的初始化方法还是非常简单的,先是根据concurrentLevel来new出Segment,这里Segment的数量是不大于concurrentLevel的最大的2的指数,就是说Segment的数量永远是2的指数个,这样的好处是方便采用移位操作来进行hash,加快hash的过程。接下来就是根据intialCapacity确定Segment的容量的大小,每一个Segment的容量大小也是2的指数,同样使为了加快hash的过程。
这边需要特别注意一下两个变量,分别是segmentShift和segmentMask,这两个变量在后面将会起到很大的作用,假设构造函数确定了Segment的数量是2的n次方,那么segmentShift就等于32减去n,而segmentMask就等于2的n次方减一。
ConcurrentHashMap 可以提供较好的并发解决方案,它的思想比hashTable 和synchronizedMap更高明一些,
使用了几个技巧来获得高程度的并发以及避免锁定,包括为不同的 hash bucket(桶)使用多个写锁和使用 JMM 的不确定性来最小化锁被保持的时间——或者根本避免获取锁。
ConcurrentHashMap 摒弃了单一的 map 范围的锁,取而代之的是由 32 个锁组成的集合,其中每个锁负责保护 hash bucket 的一个子集。锁主要由变化性操作(put() 和 remove())使用。具有 32 个独立的锁意味着最多可以有 32 个线程可以同时修改 map。这并不一定是说在并发地对 map 进行写操作的线程数少于 32 时,另外的写操作不会被阻塞——32 对于写线程来说是理论上的并发限制数目,但是实际上可能达不到这个值。但是,32 依然比 1 要好得多,而且对于运行于目前这一代的计算机系统上的大多数应用程序来说已经足够了。
大多并发类使用同步来保证独占式访问一个数据结构(以及保持数据结构的一致性)。ConcurrentHashMap 没有采用独占性和一致性,它使用的链表是经过精心设计的,所以其实现可以检测 到它的列表是否一致或者已经过时。如果它检测到它的列表出现不一致或者过时,或者干脆就找不到它要找的条目,它就会对适当的bucket 锁进行同步并再次搜索整个链。这样做在一般的情况下可以优化查找,所谓的一般情况是指大多数检索操作是成功的并且检索的次数多于插入和删除的次数。
我们看一下 get 方法实现
-
Vget(Objectkey,inthash){
-
if(count!=0){
-
HashEntry<K,V>e=getFirst(hash);
-
while(e!=null){
-
if(e.hash==hash&&key.equals(e.key)){
-
Vv=e.value;
-
if(v!=null)
-
returnv;
-
returnreadValueUnderLock(e);
-
}
-
e=e.next;
-
}
-
}
-
检索操作首先为目标 bucket 查找头指针(是在不锁定的情况下完成的,所以说可能是过时的),然后在不获取 bucket 锁的情况下遍历 bucket 链。如果它不能发现要查找的值,就会同步并试图再次查找条目。
ConcurrentHashMap 对于很多并发应用程序来说是一个非常有用的类,而且对于理解 JMM 何以取得较高性能的微妙细节是一个很好的例子。ConcurrentHashMap 是编码的经典,需要深刻理解并发和 JMM 才能够写得出。
参考以下:
http://www.ibm.com/developerworks/cn/java/java-lo-concurrenthashmap/index.html?ca=drs-
http://dingjob.iteye.com/blog/668647
http://www.iteye.com/topic/1103980
分享到:
相关推荐
java源码剖析-ConcurrentHashMap
Java利用ConcurrentHashMap实现本地缓存demo; 基本功能有缓存有效期、缓存最大数、缓存存入记录、清理线程、过期算法删除缓存、LRU算法删除、获取缓存值等功能。 复制到本地项目的时候,记得改包路径哦~
ConcurrentHashMap源码剖析
NULL 博文链接:https://sj8515465.iteye.com/blog/2045785
源码分析见我博文:http://blog.csdn.net/wabiaozia/article/details/50684556
ConcurrentHashMap可以做到读取数据不加锁,并且其内部的结构可以让其在进行写操作的时候能够将锁的粒度保持地尽量地小,允许多个修改操作并发进行,其关键在于使用了锁分离技术。它使用了多个锁来控制对hash表的...
ConcurrentHashMap具体是怎么实现线程安全的呢,肯定不可能是每个方法加synchronized,那样就变成了HashTable。
这时候ConcurrentHashMap达到容量扩容而忽略了ReservationNode情况,调用put的时候在synchronized(f)没有对ReservationNode处理,所以会出现死循环。 在jdk1.8和1.9中对比 http://gee.cs.oswego.edu/cgi- bin/...
解析concurrenthashmap的源码,学习多线程的思想
程序员面试加薪必备_ConcurrentHashMap底层原理与源码分析深入详解
ConcurrentHashMap是J.U.C(java.util.concurrent包)的重要成员,它是HashMap的一个线程安全的、支持高效并发的版本。在默认理想状态下,ConcurrentHashMap可以支持16个线程执行并发写操作及任意数量线程的读操作。...
java本地缓存ConcurrentHashMap
Java——并发容器之ConcurrentHashMap;Java——并发容器之ConcurrentHashMap;Java——并发容器之ConcurrentHashMap;Java——并发容器之ConcurrentHashMap;Java——并发容器之ConcurrentHashMap;Java——并发...
ConcurrentHashMap之实现细节
java7-8中的 HashMap和ConcurrentHashMap全解析
concurrenthashmap1.7的源码分析
ConcurrentHashMap是一个线程安全的HashTable,它的主要功能是提供了一组和HashTable功能相同但是线程安全的方法。ConcurrentHashMap可以做到读取数据不加锁,并且其内部的结构可以让其在进行写操作的时候能够将锁的...
ConcurrentHashMap的实现原理(JDK1.7和JDK1.8),并说明了在jdk1.7与jdk1.8的不同实现原理
Java并发编程之ConcurrentHashMap Java并发编程之ConcurrentHashMap.pdf
ConcurrentHashMap的实现原理(纯干货)