浅析JDK1.8 下HashMap线程安全性

Java线程安全主要体现在3个方面:可见性、原子性、有序性。下文主要从原子性和可见性分析JDK 1.8 中HashMap的线程安全性及ConcurrentHashMap如何实现线程安全。

HashMap中存在的线程安全问题:

1.HashMap在读取Hash槽首元素的时候读取的是工作内存中引用所指向的对象,并发情况下,其他线程修改的值并不能被及时读取到。

2.HashMap在插入新元素的时候,主要会进行两次判断:

2.1 第一次是根据键的hash判断当前hash槽是否被占用,如果没有就放入当前插入对象。并发情况下,如果A线程判断该槽未被占用,在执行写入操作时时间片耗尽。此时线程B也执行获取hash(恰巧和A线程的对象发生hash碰撞)判断该槽未被占用,继而直接插入该对象。然后A线程被CPU重新调度继续执行写入操作,就会将线程B的数据覆盖。(注:此处也有可见性问题)

2.2 第二次是同一个hash槽内,因为HashMap特性是保持key值唯一,所以会判断当前欲插入key是否存在,存在就会覆盖。与上文类似,并发情况下,如果线程A判断最后一个节点仍未发现重复key那么会把以当前对象构建节点挂在链表或者红黑树上,如果线程B在A判断操作和写操作之间,进行了判断和写操作,也会发生数据覆盖。

除此之外扩容也会发生类似的并发问题。还有size的问题,感觉其实高并发情况下这个size的准确性可以让步性能。

ConcurrentHashMap实现线程安全

其实观察后发现HashMap的线程安全主要体现在可见性和TestAndSet操作的非原子性上。解决这两个问题的暴力方法就是给每个方法加锁,synchronized关键字的虚拟机实现保证了原子性和可见性(具体可参考《深入理解Java虚拟机》),HashTable就是这种实现方案。

但这种加锁方式对性能损耗严重,因为锁的是整个this对象,所以读写操作都是线程阻塞的。

ConcurrentHashMap采用一种性能更好的方案,volatile关键字保证可见性,1问题得以解决。

针对2.1问题采用无锁化同步机制,即CAS(需配合volatile),如果设置成功,跳出循环返回,如果失败继续下次循环重新判断。

针对2.2问题采用synchronized加锁,因为hash槽维度已保证线程安全,故而此处只需保证槽内数据线程安全即可,所以此处加锁的对象是链表首节点或者红黑树。这样就保证了线程安全性。扩容的时候类似。

其实主要就是采用无锁化的手段减少锁的开销,只有发生哈希碰撞才会产生锁竞争,极端情况下,如hash离散性极好,甚至不会发生锁竞争。

此处只是描述了实现思路,具体实现可参考JDK源码。

扩展

ConcurrentHashMap实现确实性能比HashTable要好,但是不利于扩展,例如要增加一个putIfAbsent方法,因为HashTable是锁的HashTable对象,我们可以拿到对象对其进行加锁,然后在加锁代码块内进行判断和添加操作,实现putIfAbsent的原子操作。但我们却无法拿到ConcurrentHashMap的加锁对象,也就没办法进行这种原子操作的扩展。好消息是ConcurrentHashMap已经提供了类似putIfAbsent的常用原子方法,基本能满足需求。此处只是一个引申讨论。

以上是本人的一些浅薄理解,如有错误,请不吝指正。

参考

《深入理解Java虚拟机》

《Java Concurrency in Practice》

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,560评论 4 361
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,104评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,297评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,869评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,275评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,563评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,833评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,543评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,245评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,512评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,011评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,359评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,006评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,062评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,825评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,590评论 2 273
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,501评论 2 268

推荐阅读更多精彩内容

  • Java SE 基础: 封装、继承、多态 封装: 概念:就是把对象的属性和操作(或服务)结合为一个独立的整体,并尽...
    Jayden_Cao阅读 2,048评论 0 8
  • ConcurrencyMap 从这一节开始正式进入并发容器的部分,来看看JDK 6带来了哪些并发容器。 在JDK ...
    raincoffee阅读 539评论 0 0
  • 本系列出于AWeiLoveAndroid的分享,在此感谢,再结合自身经验查漏补缺,完善答案。以成系统。 Java基...
    济公大将阅读 1,485评论 1 6
  • 相关概念 面向对象的三个特征 封装,继承,多态.这个应该是人人皆知.有时候也会加上抽象. 多态的好处 允许不同类对...
    东经315度阅读 1,822评论 0 8
  • 九种基本数据类型的大小,以及他们的封装类。(1)九种基本数据类型和封装类 (2)自动装箱和自动拆箱 什么是自动装箱...
    关玮琳linSir阅读 1,836评论 0 47