ConcurrentHashMap中的同步

由于HashMap是非线程安全的,扩容时容易造成环形链表,导致在获取链表中不存在的值时陷入死循环。于是在HashMap在并发环境中需要加锁使用,这种方式开销大。Java中提供了另一种数据结构ConcurrentHashMap,专门用于并发环境下的Map映射需求。

具体机制:CAS 结合 synchronized,并通过sun.misc.Unsafe提供的可见性操作接口来访问数据。

主要关注以下几个地方:

  • put 放数据
  • resize 扩容
  • get 取数据

1、对于 put 操作

key值求hash后所在table中索引位置无节点(null),通过CAS方式将节点插入到该索引位置
key值求hash后所在table中索引位置有节点(不为null),通过synchronized锁住该索引位置处链表或者红黑树的第一个节点

2、对于 resize 扩容操作

多线程都能参与扩容操作,多线程同步怎么做的?

1)sizeCtl :该标签用于保证resize调整不会重叠,在初始化和resize扩容的时候,sizeCtl负值。其中 -1:表示table正在初始化 -(1+活跃的扩容线程数):表示正在扩容。其它也有默认值、初始化表时的表的size值、或者下一次扩容阈值。

每个线程在帮助扩容时,都会通过CAS来对sizeCtl进行+1操作,表示增加了一个线程帮助扩容

2)ForwardingNode:标记节点,用来进行并发控制,对旧table中标记为ForwardingNode的节点不再处理,避免重复。

3)transferIndex:扩容时旧table中下一个需要转移的一个长度单位的起始下标,不管哪个线程一次任务中就处理 MIN_TRANSFER_STRIDE 这么多个。

先来看看怎么确定某个元素put后到达table中的下标位置

i = (n - 1) & hash

n 表示 table 长度,hash 表示插入元素的哈希值,这么做是因为 n 是 2 的幂,n-1 其二进制表示中各位均为1。
(n - 1) & hash 与 hash % n 取余的效果相同。这样便将 hash 值分布到 table 中。

transfer中有一段代码不好理解,这里是将旧表中的元素移到新表中去

if (fh >= 0) {
   
  // fh & (n - 1) 决定了f节点在table中的下标,因为n是2的幂,
  // 所以 fh & n 只有两个结果,要么是 0,要么是 n
  // 所以这里是为了将该链表拆分成两个部分,结果为0的为一部分,为n的为另一部分
  // 扩容之后数组长度增加一倍,数组元素要重新分布
  // 最好的情况当然是原长度部分和扩容长度部分的元素各一半,并且尽多保持原来的顺序
  int runBit = fh & n;
  Node
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值