ConcurrentHashMap 与hashMap基本相同,只是ConcurrentHashMap是线程安全的,jdk1.8实现原理基本相似,是对Hashmap的改造。
高并发编程系列:ConcurrentHashMap的实现原理
Java:CAS(乐观锁)
Java容器(二)-CurrentHashMap详解(JDK1.8)
HashMap 和 currentHashMap JDK8总结
JDK 1.7以前
使用分段锁,一个Segment数组和多个HashEntry组成,Segment数组是将一个大的table分割成多个小的table来进行加锁,每一个Segment元素存储的是HashEntry数组+链表。
JDK1.6 优化二次Hash算法
JDK1.7:分段锁懒加载,需要才实例化,由于可见性所以大量使用volatile
JDK 1.8
摒弃了分段锁,采用了数组+链表+红黑树的实现,内部大量采用CAS操作。
原来是对需要进行数据操作的Segment加锁,现调整为对每个数组元素加锁(Node)。
CAS
CAS操作的就是乐观锁,每次不加锁而是假设没有冲突而去完成某项操作,如果因为冲突失败就重试,直到成功为止。
synchronized是悲观锁,这种线程一旦得到锁,其他需要锁的线程就挂起的情况就是悲观锁。
CAS机制当中使用了3个基本操作数:内存地址V,旧的预期值A,要修改的新值B。更新一个变量的时候,只有当变量的预期值A和内存地址V当中的实际值相同时,才会将内存地址V对应的值修改为B。
CAS的缺点:
1.CPU开销较大
在并发量比较高的情况下,如果许多线程反复尝试更新某一个变量,却又一直更新不成功,循环往复,会给CPU带来很大的压力。
2.不能保证代码块的原子性
CAS机制所保证的只是一个变量的原子性操作,而不能保证整个代码块的原子性。比如需要保证3个变量共同进行原子性的更新,就不得不使用Synchronized了。
HashMap
参考文章
new 时候没有初始化,在put时候初始化
java1.8 采用了数组+链表/红黑树的实现,链表长度大于8且节点数组长度大于64的时候,由链表转换为红黑树;红黑树节点<6 由红黑树转为链表(8和6的取值通过泊松分布取得)
树化有个要求就是数组长度必须大于等于MIN_TREEIFY_CAPACITY(64),否则继续采用扩容策略。
树化就是将原本的单链表转化为双向链表,再遍历这个双向链表转化为红黑树
static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
扰动函数,使用高位参与运算,减少Hash碰撞