1.  先来了解ConcurrentHashMap中的几个成员,当然大多数与HashMap中的相似,我们只看独有的成员

/**
* The default concurrency level for this table, used when not
* otherwise specified in a constructor.
*/
static final int DEFAULT_CONCURRENCY_LEVEL = 16; //默认的并发级别
/**
* The maximum capacity, used if a higher value is implicitly
* specified by either of the constructors with arguments. MUST
* be a power of two <= 1<<30 to ensure that entries are indexable
* using ints.
*/
static final int MAXIMUM_CAPACITY = 1 << 30; //最大容量
/**
* The minimum capacity for per-segment tables. Must be a power
* of two, at least two to avoid immediate resizing on next use
* after lazy construction.
*/
static final int MIN_SEGMENT_TABLE_CAPACITY = 2; //每个Segement中的桶的数量
/**
* The maximum number of segments to allow; used to bound
* constructor arguments. Must be power of two less than 1 << 24.
*/
static final int MAX_SEGMENTS = 1 << 16; // slightly conservative //允许的最大的Segement的数量
/**
* Mask value for indexing into segments. The upper bits of a
* key's hash code are used to choose the segment.
*/
final int segmentMask; //掩码,用来定位segements数组的位置 /**
* Shift value for indexing within segments.
*/
final int segmentShift;       //偏移量,用来确认hash值的有效位
/**
* The segments, each of which is a specialized hash table.
*/
final Segment<K,V>[] segments; //相当于多个HashMap组成的数组

  

static final class Segment<K,V> extends ReentrantLock implements Serializable {  //内部类Segment,继承了ReentrantLock,有锁的功能
/**
* The maximum number of times to tryLock in a prescan before
* possibly blocking on acquire in preparation for a locked
* segment operation. On multiprocessors, using a bounded
* number of retries maintains cache acquired while locating
* nodes.
*/
static final int MAX_SCAN_RETRIES =
Runtime.getRuntime().availableProcessors() > 1 ? 64 : 1; /**
* The per-segment table. Elements are accessed via
* entryAt/setEntryAt providing volatile semantics.
*/
transient volatile HashEntry<K,V>[] table; //每个Segement内部都有一个table数组,相当于每个Segement都是一个HashMap transient int count; //这些参数与HashMap中的参数功能相同 transient int modCount; transient int threshold; final float loadFactor; Segment(float lf, int threshold, HashEntry<K,V>[] tab) {
this.loadFactor = lf;
this.threshold = threshold;
this.table = tab;
} final V put(K key, int hash, V value, boolean onlyIfAbsent) { //向Segement中添加一个元素 }

2. 构造函数

  

@SuppressWarnings("unchecked")
public ConcurrentHashMap(int initialCapacity,
float loadFactor, int concurrencyLevel) {
if (!(loadFactor > 0) || initialCapacity < 0 || concurrencyLevel <= 0) //参数校验
throw new IllegalArgumentException();
if (concurrencyLevel > MAX_SEGMENTS)
concurrencyLevel = MAX_SEGMENTS;
// Find power-of-two sizes best matching arguments
int sshift = 0;        
int ssize = 1;     //计算segement数组的大小,并且为2的倍数,默认情况下concurrentyLevel为16,那么ssize也为16
while (ssize < concurrencyLevel) {
++sshift; //ssize每次进行左移运算,因此sshift可以看做是ssize参数左移的位数
ssize <<= 1;
}
     
this.segmentShift = 32 - sshift; //segement偏移量
this.segmentMask = ssize - 1; //由于ssize为2的倍数,所以sengemnt为全1的,用来定位segement数组的下标
if (initialCapacity > MAXIMUM_CAPACITY)
initialCapacity = MAXIMUM_CAPACITY;
      
int c = initialCapacity / ssize; //计算每个Segement中桶的数量
if (c * ssize < initialCapacity)
++c;
int cap = MIN_SEGMENT_TABLE_CAPACITY;
while (cap < c)
cap <<= 1;
// create segments and segments[0]
Segment<K,V> s0 = //初始化第一个segement
new Segment<K,V>(loadFactor, (int)(cap * loadFactor),
(HashEntry<K,V>[])new HashEntry[cap]);
Segment<K,V>[] ss = (Segment<K,V>[])new Segment[ssize]; //新建segements数组,并将s0赋值
UNSAFE.putOrderedObject(ss, SBASE, s0); // ordered write of segments[0]
this.segments = ss;
}

3 . 我们来看put()方法

@SuppressWarnings("unchecked")
public V put(K key, V value) {
Segment<K,V> s;
if (value == null)            //ConcurrentHashMap中value不能为空
throw new NullPointerException();
int hash = hash(key); //获取到key的hash值
int j = (hash >>> segmentShift) & segmentMask; //定位到某个segement位置
if ((s = (Segment<K,V>)UNSAFE.getObject //从上面的构造方法中我们知道,segments数组只有0位置的segment被初始化了,因此这里需要去检测计算出的位置的segment是否被初始化
                                      由于是并发容器,所以使用UNSAFE中的方法
(segments, (j << SSHIFT) + SBASE)) == null) // in ensureSegment
s = ensureSegment(j);
return s.put(key, hash, value, false); //将元素插入到定位的Segement中
}
final V put(K key, int hash, V value, boolean onlyIfAbsent) {   //segement中的put()方法
HashEntry<K,V> node = tryLock() ? null : //获取锁,若获取不到锁,则县创建节点并返回
scanAndLockForPut(key, hash, value);
V oldValue;
try {                               
HashEntry<K,V>[] tab = table; //之后的算法就与HashMap中相似了
int index = (tab.length - 1) & hash;
HashEntry<K,V> first = entryAt(tab, index);
for (HashEntry<K,V> e = first;;) {
if (e != null) {
K k;
if ((k = e.key) == key ||
(e.hash == hash && key.equals(k))) {
oldValue = e.value;
if (!onlyIfAbsent) {
e.value = value;
++modCount;
}
break;
}
e = e.next;
}
else {
if (node != null)
node.setNext(first);
else
node = new HashEntry<K,V>(hash, key, value, first);
int c = count + 1;
if (c > threshold && tab.length < MAXIMUM_CAPACITY)
rehash(node);
else
setEntryAt(tab, index, node);
++modCount;
count = c;
oldValue = null;
break;
}
}
} finally {
unlock(); //释放锁
}
return oldValue;
}

4. 来具体看一下SegementMask与SegmentShift这两个变量时怎么使用的?

  

     int sshift = 0;        
int ssize = 1;     //计算segement数组的大小,并且为2的倍数,默认情况下concurrentyLevel为16,那么ssize也为16
while (ssize < concurrencyLevel) {
++sshift; //ssize每次进行左移运算,因此sshift可以看做是ssize参数左移的位数
ssize <<= 1;
}
     
this.segmentShift = 32 - sshift; //segement偏移量
this.segmentMask = ssize - 1; //由于ssize为2的倍数,所以sengemnt为全1的,用来定位segement数组的下标

  上面是构造函数中计算这两个变量的代码。

  我们假设concurrencyLevel为默认值16,那么经过计算得到,ssize = 16,sshift = 4,segmentShift  = 28, segementMask = 15

  由于ssize为segements数组的大小,我们可以发现,当 n 与 segmentMask按位与时候正好可以得到<=15的数组,正是segements数组的下标。

  

 int j = (hash >>> segmentShift) & segmentMask;   //定位到某个segement位置

  而segementShift的作用在于缩小hash值的范围,我们并不需要使用hash值所有的位,通过上面的数据,当hash值右移28位后正好可以得到有效计算的位数(4位),因此上面构造函数中的sshift也

  可以表示计算segements数组时的有效位数。

ConcurrentHashMap(1.7)分析的更多相关文章

  1. Hashtable、ConcurrentHashMap源码分析

    Hashtable.ConcurrentHashMap源码分析 为什么把这两个数据结构对比分析呢,相信大家都明白.首先二者都是线程安全的,但是二者保证线程安全的方式却是不同的.废话不多说了,从源码的角 ...

  2. ConcurrentHashMap源码分析(一)

    本篇博客的目录: 前言 一:ConcurrentHashMap简介 二:ConcurrentHashMap的内部实现 三:总结 前言:HashMap很多人都熟悉吧,它是我们平时编程中高频率出现的一种集 ...

  3. ConcurrentHashMap 源码分析

    ConcurrentHashMap 源码分析 1. 前言    终于到这个类了,其实在前面很过很多次这个类,因为这个类代码量比较大,并且涉及到并发的问题,还有一点就是这个代码有些真的晦涩,不好懂.前前 ...

  4. 死磕 java集合之ConcurrentHashMap源码分析(三)

    本章接着上两章,链接直达: 死磕 java集合之ConcurrentHashMap源码分析(一) 死磕 java集合之ConcurrentHashMap源码分析(二) 删除元素 删除元素跟添加元素一样 ...

  5. ConcurrentHashMap源码分析_JDK1.8版本

    在jdk1.8中主要做了2方面的改进 改进一:取消segments字段,直接采用transient volatile HashEntry<K,V>[] table保存数据,采用table数 ...

  6. 并发-ConcurrentHashMap源码分析

    ConcurrentHashMap 参考: http://www.cnblogs.com/chengxiao/p/6842045.html https://my.oschina.net/hosee/b ...

  7. ConcurrentHashMap源码分析

    看过hashMap源码之后一直意犹未尽的感觉,挡不住我看其他的源码了.HashMap在单线程中非常好用,也不会出现什么问题,但是一到多线程就gg了,变的不灵了.我们有HashTable可以运用在多线程 ...

  8. Java并发系列[9]----ConcurrentHashMap源码分析

    我们知道哈希表是一种非常高效的数据结构,设计优良的哈希函数可以使其上的增删改查操作达到O(1)级别.Java为我们提供了一个现成的哈希结构,那就是HashMap类,在前面的文章中我曾经介绍过HashM ...

  9. ConcurrentHashMap源码分析(1.8)

    0.说明 1.ConcurrentHashMap跟HashMap,HashTable的对比 2.ConcurrentHashMap原理概览 3.ConcurrentHashMap几个重要概念 4.Co ...

  10. java基础系列之ConcurrentHashMap源码分析(基于jdk1.8)

    1.前提 在阅读这篇博客之前,希望你对HashMap已经是有所理解的,否则可以参考这篇博客: jdk1.8源码分析-hashMap:另外你对java的cas操作也是有一定了解的,因为在这个类中大量使用 ...

随机推荐

  1. MySQL分层和查询数据的流程

    MySQL分层 MySQL分层 主要分为:连接层,服务层,引擎层,存储层 客户端执行一条select命令的流程如下 连接器 功能: 负责跟客户端建立连接.获取权限.维持和管理连接 细节: 1.当用户登 ...

  2. Solr搜索引擎【索引管理】

    一.索引存储 当文档提交到索引之后,directory目录组件会将它们写入到持久化存储器.Solr的目录组件具有以下重要特点: 1.隐藏持久存储的读写细节,例如,将文档写入到磁盘或通过JDBC在数据库 ...

  3. 深入 .NET Core 基础 - 2:共享框架

    深入 .NET Core 基础 - 2:共享框架 原文地址:https://natemcmaster.com/blog/2018/08/29/netcore-primitives-2/ 共享框架从 . ...

  4. Spring Boot2 系列教程(二十九)Spring Boot 整合 Redis

    经过 Spring Boot 的整合封装与自动化配置,在 Spring Boot 中整合Redis 已经变得非常容易了,开发者只需要引入 Spring Data Redis 依赖,然后简单配下 red ...

  5. 【读一本书】《昇腾AI处理器架构与编程》--神经网络基础知识(2)

    1 卷积神经网络:输入层 之前提到多层感知机的参数太多,导致训练耗时长并且对图像处理也不具有优势,因此大神们 就提出了多层神经网络,其中最经典的是卷积神经网络(Convolution Neural N ...

  6. elasticSerach 知识学习

    一 介绍: ElasticSearch是一个基于Lucene的搜索服务器.它提供了一个分布式多用户能力的全文搜索引擎,基于RESTful web接口.Elasticsearch是用Java语言开发的, ...

  7. autoLayout+sizeClass屏幕适配

    一.屏幕适配(autoLayout+sizeClass) 1.目前市面上的主流布局形式: a. frame 布局,通过代码计算(老程序员习惯使用) b. autoLayout(新的出现)与sizeCl ...

  8. mybatis错题

    第一题 解析: MyBatis的动态SQL中没有else元素,when元素的test属性中直接书写表达式即可,即test=”表达式”. 第二题 解析: resource属性和url属性是必须的属性,但 ...

  9. Mybatis获取自动增长Id

    Mybatis获取自动增长Id MyBatis成功插入后获取自动增长的id 1.向xxMapping.xml配置中加上两个配置. <insert id="insertUser" ...

  10. 基于 H5 + WebGL 实现 3D 可视化地铁系统

    前言 工业互联网,物联网,可视化等名词在我们现在信息化的大背景下已经是耳熟能详,日常生活的交通,出行,吃穿等可能都可以用信息化的方式来为我们表达,在传统的可视化监控领域,一般都是基于 Web SCAD ...