1)相对于HashTable 整个加锁的形式,采用了分段加锁的方式,只有hash值在同一个段中的数据才会发生竞争,如果分n段,理论上最高的并发度也是n。

2)分析get操作

比较一下HashTable的get操作,是通过synchronized加了锁的

public synchronized V get(Object key) {

Entry tab[] = table;

int hash = hash(key);

int index = (hash & 0x7FFFFFFF) % tab.length;

for (Entry e = tab[index] ; e != null ; e = e.next) {

if ((e.hash == hash) && e.key.equals(key)) {

return e.value;

}

}

return null;

}

而在ConcurrentHashMap当中,是通过Segment的get操作获取的

-------------------------------------------------

问:ConcurrentHashMap的get操作有没有加锁?

答:并没有

问:那怎么实现的并发安全准确的读取数据呢?

答:当出现有key,但是没有value的情况时,将加lock锁,等待value值写入,再读取,防止读不到最新的值。

问:ConcurrentHashMap有哪些参数可以在构造方法中设置?

答:concurrencyLevel 并发度,也就是segment的段数 loadfactor 加载因子,进行扩容的阀值因子 initialCapacity 初始化总的线性链的个数,平均分配到每个segment当中

问:为什么concurrencyLevel 和每个segment当中的链表数都设置成2的n次方形式?

为了根据hash值得前缀或者后缀快速定位所属的segment和线性链

static final class HashEntry {

final K key;

final int hash;

volatile V value;

final HashEntry next;

}

HashEntry节点key hash next 都定义成final为什么? volatile修饰value为什么?

为了防止链表结构被破坏,出现ConcurrentModification的情况。

-------------------------------

//返回键值key映射的value值,当不包含key键时返回nullpublic V get(Object key) {

int hash = hash(key.hashCode());

return segmentFor(hash).get(key, hash);

}

/* Specialized implementations of map methods */

V get(Object key, int hash) {

if (count != 0) { // read-volatile HashEntry e = getFirst(hash);

while (e != null) {

if (e.hash == hash && key.equals(e.key)) {

V v = e.value;

if (v != null)

return v;

return readValueUnderLock(e); // recheck }

e = e.next;

}

}

return null;

}

Segment的get操作没有加锁又是怎么实现并发的呢?

ConcurrentHashMap的get操作是直接委托给Segment的get方法

get操作不需要锁。

第一步是访问count变量,这是一个volatile变量,由于所有的修改操作在进行结构修改时都会在最后一步写count变量,通过这种机制保证get操作能够得到最新的结构更新。

下面都是Segment内部的变量和方法

/*** The number of elements in this segment's region.*/

transient volatile int count;

/*** Returns properly casted first entry of bin for given hash.*/

HashEntry getFirst(int hash) {

HashEntry[] tab = table;

return tab[hash & (tab.length - 1)];

}

链表节点

static final class HashEntry {

final K key;

final int hash;

volatile V value;

final HashEntry next;

HashEntry(K key, int hash, HashEntry next, V value) {

this.key = key;

this.hash = hash;

this.next = next;

this.value = value;

}

@SuppressWarnings("unchecked")

static final HashEntry[] newArray(int i) {

return new HashEntry[i];

}

}

对于非结构更新,也就是结点值的改变,由于HashEntry的value变量是volatile的,也能保证读取到最新的值。

接下来就是对hash链进行遍历找到要获取的结点,如果没有找到,直接访回null。对hash链进行遍历不需要加锁的原因在于链指针next是final的。

但是头指针却不是final的,这是通过getFirst(hash)方法返回,也就是存在table数组中的值。这使得getFirst(hash)可能返回过时的头结点,

例如,当执行get方法时,刚执行完getFirst(hash)之后,另一个线程执行了删除操作并更新头结点,这就导致get方法中返回的头结点不是最新的。这是可以允许,通过对count变量的协调机制,get能读取到几乎最新的数据,虽然可能不是最新的。要得到最新的数据,只有采用完全的同步。

最后,如果找到了所求的结点,判断它的值如果非空就直接返回,否则在有锁的状态下再读一次。这似乎有些费解,理论上结点的值不可能为空,这是因为put的时候就进行了判断,如果为空就要抛NullPointerException。空值的唯一源头就是HashEntry中的默认值,因为HashEntry中的value不是final的,非同步读取有可能读取到空值。仔细看下put操作的语句:tab[index] = new HashEntry(key, hash, first, value),在这条语句中,HashEntry构造函数中对value的赋值以及对tab[index]的赋值可能被重新排序,这就可能导致结点的值为空。这种情况应当很罕见,一旦发生这种情况,ConcurrentHashMap采取的方式是在持有锁的情况下再读一遍,这能够保证读到最新的值,并且一定不会为空值。

参考于

http://www.iteye.com/topic/344876

Java8中的实现原理与Java7已经有很大不同了http://www.importnew.com/28263.html​www.importnew.com

Java怎么实现大的map_关于Java中的ConcurrentHashMap的实现原理有大神可以详细介绍下吗?...相关推荐

  1. mc服务器java占用过大_如何排查java应用中CPU使用率高或内存占用高的问题

    点击上方"Java知音",选择"置顶公众号" 技术文章第一时间送达! 作者:MnameHZJ my.oschina.net/u/1420452/blog/224 ...

  2. java抓新闻_【图片】【抓取】Java获取各大网站新闻【java吧】_百度贴吧

    该楼层疑似违规已被系统折叠 隐藏此楼查看此楼 下面上源码,用一小时做的,还有些bug package Getnews; import java.io.*; import java.net.*; imp ...

  3. java抓取新闻_【图片】【抓取】Java获取各大网站新闻【java吧】_百度贴吧

    该楼层疑似违规已被系统折叠 隐藏此楼查看此楼 下面上源码,用一小时做的,还有些bug package Getnews; import java.io.*; import java.net.*; imp ...

  4. java 抽象类与接口区别是什么_JAVA中抽象类与接口的区别,分别在什么情况下使用它们...

    在网上看到很多人问关于"抽象类与接口的区别",因此本人想通过自己多年对JAVA开发的经验来总结一下抽象类与接口的区别以及分别在什么情况下使用它们. 在Java语言中, abstra ...

  5. java list compareto_java中的compareto方法以及LIst列表排序的详细介绍【转】

    /*测试类*/ package test; import java.util.ArrayList; import java.util.Collections; import java.util.Com ...

  6. java中finish什么意思,finish是什么意思(详细介绍)

    单词音标 英语音标:[ˈfɪnɪʃ] 美语音标:[ˈfɪnɪʃ] 中文翻译 v.完成:结束:最后加工:用完 n.结束:完成:最后一层涂饰 单词例句 用作动词 (v.) When do you fini ...

  7. java 常用类 - 大数据运算

    java 常用类 - 大数据运算 在 Java中提供了用于大数字运算的类,即 java.math.BigInteger 类和 java.math.BigDecimal 类.这两个类用于高精度计算,其中 ...

  8. java实现账号单一ip登录,使用Java实现简单后台访问并获取IP示例

    使用Java实现简单后台访问并获取IP示例 发布时间:2020-10-28 21:57:57 来源:亿速云 阅读:92 作者:Leah 使用Java实现简单后台访问并获取IP示例?针对这个问题,这篇文 ...

  9. java虚拟机_深入浅出Java虚拟机,从来没有人能把Java 虚拟机讲解的这么透彻

    首先:我们要知道为什么要去学习Java虚拟机 Java 虚拟机提供了许多配置参数,用于满足不同应用场景下,对程序性能的需求.学习 Java 虚拟机,你可以针对自己的应用,最优化匹配运行参数.(你可以用 ...

最新文章

  1. 干货 | 成为一名推荐系统工程师永远都不晚
  2. Android FM模块学习之一 FM启动流程
  3. python123程序设计题说句心里话_用c++写一个简单的计算器程序
  4. Git篇——Git使用教程
  5. 电工助手App开发 第三周
  6. Hadoop之HDFS面试知识复习
  7. C#多线程开发-使用并发集合
  8. 小程序分享到朋友圈_如何给小程序添加分享朋友圈
  9. mysql 视图 字典_MySQL深入01-SQL语言-数据字典-服务器变量-数据操作DML-视图
  10. html的frame加背景图片,tkinter第二章(添加图片,背景图片)
  11. 当前操作系统缺少黑体等字体_2020世界互联网大会:360视觉、360守望领域、360城市运营操作系统齐亮相...
  12. 基于R语言实现的交通时空大数据处理
  13. POJ2139 Six Degrees of Cowvin Bacon【Floyd算法】
  14. Mac上使用CleanMyMac彻底擦除文件详细教程
  15. 手把手教你逆向分析 Android 程序
  16. pandas读取excel遇见空值的处理
  17. 关于SQLite创建视图
  18. 有关南怀瑾、朱熹周易断卦法的个人观点
  19. 阿里测开岗定级P7全流程加面试真题
  20. 智能剪刀——交互式图像分割

热门文章

  1. 华为入局 VR 眼镜能让 VR 早普及几年?|CSDN博文精选
  2. Go 语言发布 2018 调查报告,最被诟病问题竟然是......
  3. 程序员崩溃了,想拿的年终奖怎么说黄就黄?!
  4. 如何更好地玩转 GitHub?
  5. 漫画 | 从搬家到容器技术 Docker 应用场景解析
  6. 95 后程序员一出校门就拿年薪 32 万?
  7. GitHub:为什么我们最终选择放弃了 jQuery
  8. Airbnb 弃用之后,我们还应该用 React Native 吗?
  9. android屏幕共享实现方式,基于WebRtc在Android端实现屏幕共享
  10. iphone11没有电池小组加件_iPhone 11系列工信部数据:全系4GB运存电池最大达3969mAh...