采用Hashtable
调用Collections.synchronizeMap()
方法来让HashMap具有多线程能力
采用ConcurrentHashMap
前两个方案的思路是相似的,均是每个方法中,对整个对象进行上锁。Hashtable是老一代的集合框架,很多的设计均以及落后,他在每一个方法中均加上了synchronize
关键字保证线程安全
第二种方法是返回一个SynchronizedMap
对象,这个对象默认每个方法会锁住整个对象。如下源码:
这里的mutex是什么呢?直接看到构造器:
可以看到默认锁的就是本身,效果和Hashtable其实是一样的。这种简单粗暴锁整个对象的方式造成的后果是:
锁是非常重量级的,会严重影响性能。
同一时间只能有一个线程进行读写,限制了并发效率。
ConcurrentHashMap的设计就是为了解决此问题。他通过降低锁粒度+CAS的方式来提高效率。简单来说,ConcurrentHashMap锁的并不是整个对象,而是一个数组的一个节点,那么其他线程访问数组其他节点是不会互相影响,极大提高了并发效率;同时ConcurrentHashMap读操作并不需要获取锁,如下图:
关于ConcurrentHashMap和Hashtable的更多内容,限于篇幅,我会在另一篇文章讲解。
那么,使用了上述的三种解决方案是不是绝对线程安全?先观察下面的代码:
当Thread1调用containsKey之后释放锁,Thread2获得锁并把“abc”移除再释放锁,这个时候Thread1读取到的s就是一个null了,也就出现了问题了。所以ConcurrentHashMap
类或者Collections.synchronizeMap()
方法或者Hashtable都只能在一定的限度上保证线程安全,而无法保证绝对线程安全。
关于线程安全,还有一个fast-fail问题,即快速失败。当使用HashMap的迭代器遍历HashMap时,如果此时HashMap发生了结构性改变,如插入新数据、移除数据、扩容等,那么Iteractor会抛出fast-fail异常,防止出现并发异常,在一定限度上保证了线程安全。如下源码:
创建Iteractor对象时会记录HashMap的modCount
变量,每当HashMap发生结构性改变时,modCount
会加1。在迭代时判断HashMap的modCount
和自己保存的expectedModCount
是否一致即可判断是否发生了结构性改变。
fast-fail异常只能当做遍历时的一种安全保证,而不能当做多线程并发访问HashMap的手段。若有并发需求,还是需要使用上述的三种方法。
小结
- HashMap并不能保证线程安全,在多线程并发访问下会出现意想不到的问题,如数据丢失等
- HashMap1.8采用尾插法进行扩容,防止出现链表环导致的死循环问题
- 解决并发问题的的方案有
Hashtable
、Collections.synchronizeMap()
、ConcurrentHashMap
。其中最佳解决方案是ConcurrentHashMap
- 上述解决方案并不能完全保证线程安全
- 快速失败是HashMap迭代机制中的一种并发安全保证
)关键变量的理解
HashMap源码中有很多的内部变量,这些变量会在下面源码分析中经常出现,首先需要理解这些变量的意义。
)扩容
HashMap源码中把初始化操作也放到了扩容方法中,因而扩容方法源码主要分为两部分:确定新的数组大小、迁移数据。详细的源码分析如下,我打了非常详细的注释,可选择查看。扩容的步骤在上述已经讲过了,读者可以自行结合源码,分析HashMap是如何实现上述的设计。
)添加数值
调用put()
方法添加键值对,最终会调用putVal()
来真正实现添加逻辑。代码解析如下:
代码中关于每个步骤有了详细的解释,这里来总结一下:
总体上分为两种情况:找到相同的key和找不到相同的key。找了需要判断是否更新并返回旧value,没找到需要插入新的Node、更新节点数并判断是否需要扩容。
查找分为三种情况:数组、链表、红黑树。数组下标i位置不为空且不等于key,那么就需要判断是否树节点还是链表节点并进行查找。
链表到达一定长度后需要扩展为红黑树,当且仅当链表长度>=8且数组长度>=64。
最后画一张图总体再加深一下整个流程的印象:
看完上述知识点如果你深感Java基础不够扎实,或者刷题刷的不够、知识不全面
小编专门为你量身定制了一套<Java一线大厂高岗面试题解析合集:JAVA基础-中级-高级面试+SSM框架+分布式+性能调优+微服务+并发编程+网络+设计模式+数据结构与算法>
针对知识面不够,也莫慌!还有一整套的<Java核心进阶手册>,可以瞬间查漏补缺
全都是一丢一丢的收集整理纯手打出来的——收整在***【我的学习笔记大全】***,有需要的朋友可以自取
更有纯手绘的各大知识体系大纲,可供梳理:Java筑基、MySQL、Redis、并发编程、Spring、分布式高性能架构知识、微服务架构知识、开源框架知识点等等的xmind手绘图~