2.Java集合-ConcurrentHashMap实现原理及源码分析

一、为何用ConcurrentHashMap

  在并发编程中使用HashMap可能会导致死循环,而使用线程安全的HashTable效率又低下。

  线程不安全的HashMap

  在多线程环境下,使用HashMap进行put操作会引起死循环,导致CPU利用率接近100%,所以在并发情况下不能使用HashMap

  效率低下的HashTable

  Hashtable使用synchronized来保证线程的安全,但是在线程竞争激烈的情况下Hashtable的效率非常低下。当一个线程访问Hashtable的同步方法,其他方法访问Hashtable的同步方法时,会进入阻塞或者轮询状态。如果线程1使用put进行元素添加,线程2不但不能用put方法添加于元素同是也无法用get方法来获取元素,所以竞争越激烈效率越低。

  ConcurrentHashMap的锁分段技术

  Hashtable容器在竞争激烈的并发环境效率低下的原因是所有访问Hashtable的线程都必须竞争同一把锁,假如容器有多把锁,每一把锁用于锁住容器中一部分数据,那么多线程访问容器里不同数据段的数据时,线程间就不会存在锁竞争,从而可以有效提高并发访问率,这就是ConcurrentHashMap的锁分段技术。将数据分成一段一段的存储,然后给每一段数据配一把锁,当一个线程占用锁访问其中一段数据的时候,其他段的数据也能被其他线程访问。

二、结构解析

  ConcurrentHashMap和Hashtable主要区别就是围绕着锁的粒度以及如何锁,可以简单理解成把一个大的Hashtable分解成多个,形成了锁分离

  感觉一个segment 就相当于一个 Hashtable

二、应用场景

  当有一个大数组时需要在多个线程共享时就可以考虑是否把它给分层多个节点了,避免大锁。并可以考虑通过hash算法进行一些模块定位

  其实不止用于线程,当设计数据表的事务时(事务事务某种意义上也是同步机制的体现),可以把一个表看成一个需要同步的数组,如果操作的表数据太多就可以考虑事务分离了(这也是为什么要避免大表的出现),比如把数据进行字段拆分、水平分表等

三、源码解读

  从上图可以看出,ConcurrentHashMap内部分为很多个Segment,每一个Segment拥有一把锁,然后每个Segment(继承ReentrantLock)下面包含很多个HashEntry列表数据。对于一个key,需要经过三次(为什么要hash三次?后面解释)hash操作,才能最终定位这个元素的位置,这三次hash分别为:

  1.对于一个key,先进行一次hash操作,得到hash值h1,也即h1 = hash1(key)

  2.将得到的h1的高几位进行第二次hash,得到hash值h2,也即h2 = hash2(h1高几位),通过h2能够确定该元素放在哪个Segment

  3.将得到的h1进行第三次hash,得到hash值h3,也即h3=hash3(h1),确定h3能够确定该元素放置在哪个HashEntry

注:在使用key定位Segment之前进行的那次hash操作,即第一次hash, 这次hash的主要目的是为了减少哈希冲突,使元素能够均匀的分布在不同的Segment上,从而提高容器的存取效率。假如哈希的质量差到极点,那么所有的元素都在一个Segment中,不仅存取元素缓慢,分段锁也会失去意义

  ConcurrentHashMap核心方法特点阐述

  ConcurrentHashMap完全允许多个读操作并发进行,读操作并不需要加锁。如果使用传统的技术,如HashMap中的实现,如果允许可以在hash链的中间添加或删除元素,读操作不加锁将得到不一致的数据。ConcurrentHashMap的实现技术是保证 HashEntry几乎是不可变的,为了确保读操作能够看到最新的值,将value设置成volatile;;下面是HashEntry的结构

1. static final class HashEntry<K,V> {
2.     final K key;
3.     final int hash;
4.     volatile V value;
5.     final HashEntry<K,V> next;
6. } 

  可以看到除了value 不是final的,其他值都是final的,这意味着不能从hash链的中间或尾部添加或删除节点,因为这需要修改 next 的引用值,所有的节点的修改只能从头部开始

  对于put操作,可以一律添加到Hash链的头部但是对于remove操作,可能需要从中间删除一个节点,这就需要将要删除节点的前面所有节点整个复制一遍,最后一个节点指向要删除结点的下一个结点。这在讲解删除操作时还会详述。为了确保读操作能够看到最新的值,将value设置成volatile,这避免了加锁。

  

  ConcurrentHashMap的数据成员及其作用阐述:

 1  public class ConcurrentHashMap<K, V> extends AbstractMap<K, V>
 2          implements ConcurrentMap<K, V>, Serializable {
 3      /**
 4       * Mask value for indexing into segments. The upper bits of a
 5       * key‘s hash code are used to choose the segment.
 6 */
 7      final int segmentMask;
 8  9      /**
10       * Shift value for indexing within segments.
11 */
12      final int segmentShift;
13 14      /**
15       * The segments, each of which is a specialized hash table
16 */
17      final Segment<K,V>[] segments;
18  }

  所有的成员都是final的,其中segmentMask和segmentShift主要是为了定位段

1. final Segment<K,V> segmentFor(int hash) {
2.     return segments[(hash >>> segmentShift) & segmentMask];
3. } 

  

  每个Segment相当于一个小的 Hashtable,它的数据成员如下:

1.     static final class Segment<K,V> extends ReentrantLock implements Serializable {
2. private static final long serialVersionUID = 2249069246763182397L;
3.         /**
4.          * The number of elements in this segment‘s region.
5.          */
6.         transient volatile int count;
7.
8.         /**
9.          * Number of updates that alter the size of the table. This is
10.          * used during bulk-read methods to make sure they see a
11.          * consistent snapshot: If modCounts change during a traversal
12.          * of segments computing size or checking containsValue, then
13.          * we might have an inconsistent view of state so (usually)
14.          * must retry.
15.          */
16.         transient int modCount;
17.
18.         /**
19.          * The table is rehashed when its size exceeds this threshold.
20.          * (The value of this field is always <tt>(int)(capacity *
21.          * loadFactor)</tt>.)
22.          */
23.         transient int threshold;
24.
25.         /**
26.          * The per-segment table.
27.          */
28.         transient volatile HashEntry<K,V>[] table;
29.
30.         /**
31.          * The load factor for the hash table.  Even though this value
32.          * is same for all segments, it is replicated to avoid needing
33.          * links to outer object.
34.          * @serial
35.          */
36.         final float loadFactor;
37. }

  count用来统计该段数据的个数,它是volatile,它用来协调修改和读取操作,以保证读取操作能够读取到几乎最新的修改。协调方式是这样的:每次修改操作做了结构上的改变,如增加/删除节点(修改节点的值不算结构上的改变),都要写count值,每次读取操作之前都要读取count的值

  

  

时间: 2024-08-08 01:29:35

2.Java集合-ConcurrentHashMap实现原理及源码分析的相关文章

1.Java集合-HashMap实现原理及源码分析

哈希表(Hash  Table)也叫散列表,是一种非常重要的数据结构,应用场景及其丰富,许多缓存技术(比如memcached)的核心其实就是在内存中维护一张大的哈希表,而HashMap的实现原理也常常出现在各类的面试题中,这里对java集合框架中的对应实现HashMap的实现原理进行讲解,然后对JDK7的HashMap的源码进行分析 哈希算法,是一类算法: 哈希表(Hash  Table)是一种数据结构: 哈希函数:是支撑哈希表的一类函数: HashMap 是 Java中用哈希数据结构实现的Ma

6.Java集合-LinkedList实现原理及源码分析

Java中LinkedList的部分源码(本文针对1.7的源码) LinkedList的基本结构 jdk1.7之后,node节点取代了 entry ,带来的变化是,将1.6中的环形结构优化为了直线型链表结构,从双向循环链表变成了双向链表 在LinkedList中,我们把链子的"环"叫做"节点",每个节点都是同样的结构.节点与节点之间相连,构成了我们LinkedList的基本数据结构,也是LinkedList的核心. 我们再来看一下LinkedList在jdk1.6和

ConcurrentHashMap实现原理及源码分析

ConcurrentHashMap实现原理 ConcurrentHashMap源码分析 总结 ConcurrentHashMap是Java并发包中提供的一个线程安全且高效的HashMap实现(若对HashMap的实现原理还不甚了解,可参考我的另一篇文章HashMap实现原理及源码分析),ConcurrentHashMap在并发编程的场景中使用频率非常之高,本文就来分析下ConcurrentHashMap的实现原理,并对其实现原理进行分析(JDK1.7). ConcurrentHashMap实现原

HashMap和ConcurrentHashMap实现原理及源码分析

ConcurrentHashMap是Java并发包中提供的一个线程安全且高效的HashMap实现,ConcurrentHashMap在并发编程的场景中使用频率非常之高,本文就来分析下ConcurrentHashMap的实现原理,并对其实现原理进行分析(JDK1.7). ConcurrentHashMap实现原理 众所周知,哈希表是中非常高效,复杂度为O(1)的数据结构,在Java开发中,我们最常见到最频繁使用的就是HashMap和HashTable,但是在线程竞争激烈的并发场景中使用都不够合理.

Java集合框架之一:ArrayList源码分析

版权声明:本文为博主原创文章,转载请注明出处,欢迎交流学习! ArrayList底层维护的是一个动态数组,每个ArrayList实例都有一个容量.该容量是指用来存储列表元素的数组的大小.它总是至少等于列表的大小.随着向 ArrayList 中不断添加元素,其容量也自动增长. ArrayList不是同步的(也就是说不是线程安全的),如果多个线程同时访问一个ArrayList实例,而其中至少一个线程从结构上修改了列表,那么它必须保持外部同步,在多线程环境下,可以使用Collections.synch

Java集合(12)--TreeSet源码分析

TreeSet 底层实际使用的存储容器就是 TreeMap,他们的关系就像HashMap和HashSet的关系. TreeSet采用了TreeMap作为其Map保存“键-值”对,所以TreeSet判断元素重复是依靠Comparable接口或Comparator接口实现的.

【Spring】Spring&amp;WEB整合原理及源码分析

表现层和业务层整合: 1. Jsp/Servlet整合Spring: 2. Spring MVC整合SPring: 3. Struts2整合Spring: 本文主要介绍Jsp/Servlet整合Spring原理及源码分析. 一.整合过程 Spring&WEB整合,主要介绍的是Jsp/Servlet容器和Spring整合的过程,当然,这个过程是Spring MVC或Strugs2整合Spring的基础. Spring和Jsp/Servlet整合操作很简单,使用也很简单,按部就班花不到2分钟就搞定了

【Spring】Spring&amp;WEB整合原理及源码分析(二)

一.整合过程 Spring&WEB整合,主要介绍的是Jsp/Servlet容器和Spring整合的过程,当然,这个过程是Spring MVC或Strugs2整合Spring的基础. Spring和Jsp/Servlet整合操作很简单,使用也很简单,按部就班花不到2分钟就搞定了,本节只讲操作不讲原理,更多细节.原理及源码分析后续过程陆续涉及. 1. 导入必须的jar包,本例spring-web-x.x.x.RELEASE.jar: 2. 配置web.xml,本例示例如下: <?xml vers

【转】HashMap实现原理及源码分析

哈希表(hash table)也叫散列表,是一种非常重要的数据结构,应用场景极其丰富,许多缓存技术(比如memcached)的核心其实就是在内存中维护一张大的哈希表,而HashMap的实现原理也常常出现在各类的面试题中,重要性可见一斑.本文会对java集合框架中的对应实现HashMap的实现原理进行讲解,然后会对JDK7中的HashMap源码进行分析. 一.什么是哈希表 在讨论哈希表之前,我们先大概了解下其它数据结构在新增.查找等基础操作上的执行性能. 数组:采用一段连续的存储单元来存储数据.对