java并发-ConcurrentMap
java并发-ConcurrentMap
背景描述:
最近在深入学习Java并发,看了几篇不错的文章,记个笔记
1.HashMap
- https://www.jianshu.com/p/a17b4717a721
- https://blog.csdn.net/stone_tomcate/article/details/100110453
(1)采用哈希表,使用数组+链表的形式进行存储。由于hash()方法可能产生哈希碰撞且会被重写,则使用链表存储hash碰撞的数据。
(2)计算hashmap的初始长度时,需要计算一个2的N次幂大于等于指定数值,后续扩容都是2倍。hashmap中经常用到与运算计算hash值(提高效率)。当数组的长度为2的n次方时,存储数据的数组的length必为偶数,length-1必为奇数,而奇数的二进制最后一位必为1,则hash值与奇数与运算时既有可能得到奇数,也有可能得到偶数;反之,如果length为奇数,则lenth-1为偶数,其二进制最后一位为0,与任意数进行与运算最后一位都为0,永远都是偶数。相当于浪费了数组一半的空间,更容易形成hash冲突。
2.ConcurrentMap
在多线程情况下,同时A、B两个线程走到createEntry()方法中,并且这两个线程中插入的元素hash值相同,bucketIndex值也相同,那么无论A线程先执行,还是B线程先被执行,最终都会2个元素先后向链表的头部插入,导致互相覆盖,致使其中1个线程中的数据丢失。这样就造成了HashMap的线程不安全,数据的不一致;
更要命的是,HashMap在多线程情况下还会出现死循环的可能,造成CPU占用率升高,导致系统卡死。
- HashTable
与HashMap不同的是,在HashTable中,所有的方法都加上了synchronized锁,用锁来实现线程的安全性。由于synchronized锁加在了HashTable的每一个方法上,所以这个锁就是HashTable本身--this。那么,可想而知HashTable的效率是如何,安全是保证了,但是效率却损失了。
- java 1.7ConcurrentMap
在JDK1.7版本中,ConcurrentHashMap的数据结构是由一个Segment数组和多个HashEntry组成,主要实现原理是实现了锁分离的思路解决了多线程的安全问题。
Segment数组的意义就是将一个大的table分割成多个小的table来进行加锁,也就是上面的提到的锁分离技术,而每一个Segment元素存储的是HashEntry数组+链表,这个和HashMap的数据存储结构一样。
- java 1.8ConcurrentMap
DK1.8的实现已经摒弃了Segment的概念,而是直接用Node数组+链表+红黑树的数据结构来实现,并发控制使用Synchronized和CAS来操作,整个看起来就像是优化过且线程安全的HashMap,虽然在JDK1.8中还能看到Segment的数据结构,但是已经简化了属性,只是为了兼容旧版本。
Node是ConcurrentHashMap存储结构的基本单元,继承于HashMap中的Entry,用于存储数据,Node数据结构很简单,就是一个链表,但是只允许对数据进行查找,不允许进行修改。