cas是乐观锁,
concurrentHashMap,并发级别(支持多大的并发量),大意是分几段
比如(8,0.75,4),就是八个桶分四段,每两个桶公用一个锁
先通过哈希算法定位到 Segment,再通过哈希算法定位到哪个HashEntry
Segment不扩容,扩容的是HashEntry,扩容是针对某个Segment里面的对象进行扩容,跟其他的Segment没有关系
Segment ssize----也是2的幂----&(segmentMask=ssize-1)操作道理同HashMap
除法算每个Segment里面有几个HashEntry(cap),一个Segment里面最少有2个HashEntry
最开始初始化的Segment是 s0->null->null->null 的
就算有多个线程同时进行put操作,在初始化数组时使用了乐观锁CAS操作来决定到底是哪个线程有资格进行初始化,其他线程均只能等待。
用到的并发技巧:
volatile变量(sizeCtl):它是一个标记位,用来告诉其他线程这个坑位有没有人在,其线程间的可见性由volatile保证。
CAS操作:CAS操作保证了设置sizeCtl标记位的原子性,保证了只有一个线程能设置成功
某个位置新建Segment对象的时候,使用循环CAS来保证线程安全,因为最开始除了头节点的Segment不为null,其他都是null
put的时候都是先加锁,再put,在解锁(包括扩容)
循环cas的时候,while在没获取锁的时候可以做一些别的事情,比如创建一个HashEntry(key相等的不用创建,cas的时候会遍历链表判断是不是有key存在相同),再比如偶数的时候重新获取头节点判断是否有别的线程已经获取锁并且插入了数据
1.7与1.8区别:
1、ConcurrentHashMap是怎么做到线程安全的?
1.7是CAS+ReentrantLock,也就是锁分段技术
1.8是CAS+synchronized
2、锁的粒度:1.7是对需要进行数据操作的Segment加锁,1.8调整为对每个数组元素加锁(Node)
3、红黑树,如果数组长度不到64,则不转红黑树,直接扩容数组
4、查询时间复杂度:从原来的遍历链表O(n),变成遍历红黑树O(logN)
5、由Segment调整为数组+链表/红黑树
ReentrantLock可重入锁里面涉及的方法
tryLock()----尝试去加锁,马上就返回,不阻塞----不能拿到锁返回false
lock()----不管能不能直接加锁,不能加锁直接阻塞
网友评论