在Concurrent1.8中, 取消了segment, 直接对Table[]加锁(换了名字, 几乎是1.7中的hashEntry). 当node数量大于8的时候, 转为红黑树, 而当红黑树数量小于6的时候, 转回链表.
核心数据结构
在Node中, 拥有value和next,都是volatile,可以看见最新的值. 还有两个数据结构
TreeNode 和 TreeNodeBin
TreeNode extends Node
TreeNodebin(红黑树的容器, 包含红黑树的根节点, 还有指针来确立是否为读写锁)
ForwardingNode(在扩容的时候使用, 先放到要扩容的地点)
private transient volatile int sizeCtl;
当为负数时,-1 表示正在初始化,-N 表示 N - 1 个线程正在进行扩容;
当为 0 时,表示 table 还没有初始化;
当为其他正数时,表示初始化或者下一次进行扩容的大小。
核心方法
tabAt(), casTabAt(), setTabAt(), native级方法来保证数据的有效性.
intiliaze(): 不会initiliaze()某个node, 而是大致地initialize HashMap设置参数, 并没有在内存中加入object, 而真正对table进行初始化则是在每一个put()操作中, 查看是否会初始化.
get():找到是哪个tab(node), 然后根据红黑树还是链表进行查找.
如果遇到扩容时,会调用标记正在扩容结点 ForwardingNode.find()方法, 查找该结点,匹配就返回
putVal():
1.检查tab是不是空的, 初始化tab, 用CAS的方法初始化tab, 如果失败, 让出CPU执行权.
2.检查是否在扩容,如果在扩容,线程帮助扩容。
3.如果没有头部节点, 则通过CAS添加头部节点.
4.如果都不是,synchronize锁住table节点(头部节点),尾插法,插在链表或者树的尾端.
remove(): 找到tab,检查是否需要红黑树转链表.
transfer()(扩容): 建立一个新的table, 遍历表中的每个node通过rehash复制到新表中.
对每一个node进行重新hash值,并且是并发扩容, 每一个线程只做一个范围内的节点, 做好之后变成forwardingNode. 更新sizeCtl为新容量的0.75倍数.
size(): 在remove和put()中对size进行实时更新, 调用addCount()计算方法, 通过CAS的方法更新.(//sumCount()才是真正实现方法, 但是具体有两个参数和CAS操作, 就算写了也记不起来, 就按上面的来记)
网友评论