ConcurrentHashMap【Java】

来自智得网
跳转至: 导航、​ 搜索

简介

HashMap是Java中最经常使用的Hash表,但是其是线程不安全的集合类,在并发场景下数据会出现脏读等,而且头插法实现的HashMap在rehash阶段还可能导致死循环,Java中并发安全的Hash表一般有三种方式:Collections.synchronizedMap(Map)、HashTable以及ConcurrentHashMap。

实现

ConcurrentHashMap的结构和HashMap基本一致

ConcurrentHashMap的存储结构和HashMap基本一致,也是数组+链表+红黑树进行存储。

不同的是,ConcurrentHashMap为了保证线程安全,采取了很多技术手段。

线程安全

JDK1.8对ConcurrentHashMap的实现进行了较大大改造。

在JDK1.7及以前,ConcurrentHashMap采用Segment分段锁的机制,对整个数组进行分段,每段都是由HashEntry对象连接而成的链表,称为Segment。

每个分段都有一个单独的锁,Segment分段锁,分段锁继承自ReentrantLock,其同步机制只会对一个分段生效,不同数据段之间的数据不会存在竞争关系,从而提高了并发的访问率。

ConcurrentHashMap在JDK1.8中取消了Segment分段锁机制,采用Node+CAS+Synchronized实现线程安全。

在数据写入时,没有Hash冲突直接通过CAS乐观锁机制进行加入,写入失败进行自旋。如果存在 Hash 冲突,就通过加锁的方式进行插入,从而保证线程安全。(如果是链表就按照尾插法插入,如果是红黑树就按照红黑树的数据结构进行插入);

初始化

ConcurrentHashMap用于存储数据的数组在进行初始化时通过自旋锁、CAS以及双重 check 等手段保证了线程池安全。

首先通过自旋锁来保证只有一个线程来进行数据初始化,

通过 CAS 设置 SIZECTL 变量的值,来保证数组初始化的正确性

CAS 成功之后,会通过双重check判断当前数组是否已经初始化完成,避免数据被多次初始化。