一、ConcurrentHashMap是什么?
ConcurrentHashMap是线程安全的并发容器,是用来替代在多线程环境下的HashMap,因为HashMap是线程不安全的,多线程环境下put操作可能会导致死循环,CPU利用率上升到100%。虽然有同步容器Hashtable和Collections.synchronizedMap方法使得容器同步,但是这些容器效率都非常低,因为它们锁的是整个容器,而ConcurrentHashMap采用的是分段锁,将同一个容器的划分为好多段,某个线程执行某些操作只会获取一个段的锁,而不会影响其他线程获取其他锁的段。
二、ConcurrentHashMap的结构。
ConcurrentHashMap把整个容器很为许多个Segment(段),段数就是并发度,每个Segment类似于一个Hashtable,管理一个HashEntry数组,每个HashEntry是一个链表头,或者红黑树的根,其实Segment的数据结构类似于HashMap,学过HashMap的底层原理就更加容易理解ConcurrentHashMap,HashMap原理讲解链接点击打开链接。
分段锁具体体现在哪里?
Segment继承ReentrantLock,这样就很容易对每个Segment加锁了。类似于get或remove这些操作,都只需要在操作前对一个Segment加锁。但是有些操作需要跨段,比如size()、containsValue()和isEmpty()方法,因此为了保证并发效率,允许size返回的是一个近似值而不是精确值。ConcurrentHashMap提供的迭代器不会抛出ConcurrentModificationException,因此不需要在迭代过程中对容器加锁。即迭代器具有弱一致性,而非及时失败。弱一致性的迭代器可以容忍并发的修改,当创建迭代器时会遍历已有的元素,可以但不保证在迭代器被构造后将修改操作反映给容器。
(图片来自网络)
三、核心操作
1、哈希定位操作。
(1)首先对key进行hash运算,得到的值为h1。即h1 = hash(key)。
(2)对h1的高几位进行hash操作,得到的值几位Segment所在的位置。h2 = hash(h1高位)
(3)对h1进行hash操作,得到的值即为该Segment中HashEntry所在的位置。h3 = hash(h1)
2、size()操作。
(1)在不对任何Segment加锁的情况下,遍历每个Segment,并且求和,如果三次中有相邻两次求得的每个Segment都相等,那么说明在这两次期间,每个Segment都没有被更新,那么可以直接返回size。
(2)若三次都不相等,那么就全部加锁,求和。
四、注意
1、key和value都不能为null。
2、虽然ConcurrentHashMap是线程安全的,但不保证所有操作都是线程安全的