写点什么

ConcurrentHashMap 源码分析 - 初始化

作者:zarmnosaj
  • 2022 年 6 月 12 日
  • 本文字数:1235 字

    阅读完需:约 4 分钟

数组初始化时的线程安全

数组初始化时,首先会通过自旋来保证初始化成功,然后通过 CAS 设置变量的值,保证同一时刻只能有一个线程对数组进行初始化,CAS 成功之后,还会对当前数组是否已经初始化完成进行判断,如果初始化完成,就不会再次初始化,整体是通过自旋 + CAS + 双重 check 等手段保证了数组初始化时的线程安全。


private final Node<K,V>[] initTable() {    Node<K,V>[] tab; int sc;    while ((tab = table) == null || tab.length == 0) {        if ((sc = sizeCtl) < 0)            Thread.yield(); // lost initialization race; just spin        else if (U.compareAndSwapInt(this, SIZECTL, sc, -1)) {            try {                if ((tab = table) == null || tab.length == 0) {                    int n = (sc > 0) ? sc : DEFAULT_CAPACITY;                    @SuppressWarnings("unchecked")                    Node<K,V>[] nt = (Node<K,V>[])new Node<?,?>[n];                    table = tab = nt;                    sc = n - (n >>> 2);                }            } finally {                sizeCtl = sc;            }            break;        }    }    return tab;}
复制代码


private final Node<K,V>[] initTable() 初始化 table,通过对 sizeCtl 的变量赋值来保证数组只能被初始化一次


while ((tab = table) == null || tab.length == 0) { } 通过自旋保证初始化成功


if ((sc = sizeCtl) < 0) 小于 0 代表有线程正在初始化,释放当前 CPU 的调度权,重新发起锁的竞争


else if (U.compareAndSwapInt(this, SIZECTL, sc, -1)) { } CAS 赋值保证当前只有一个线程在初始化,-1 代表当前只有一个线程能初始化,保证了数组的初始化的安全性


if ((tab = table) == null || tab.length == 0) { } 很有可能执行到这里的时候,table 已经不为空了,这里是双重 check


int n = (sc > 0) ? sc : DEFAULT_CAPACITY; 进行初始化


总结:


  1. 通过自旋死循环保证一定可以新增成功。 在新增之前,通过 for (Node<K,V>[] tab = table;;) ,用死循环的写法来保证新增一定可以成功,只要新增成功后,就可以退出当前死循环,新增失败的话,会重复继续循环,执行新增的步骤,直到新增成功为止。

  2. 当前槽点为空时,通过 CAS 新增。源码没有在判断槽点为空的情况下直接赋值,因为在判断槽点为空和赋值的这个时间段,槽点有可能已经被其他线程赋值了,所以这里采用了 CAS 的思想,能够保证槽点为空的情况下赋值成功,如果恰好槽点已经被其他线程赋值,当前 CAS 操作失败,会再次执行 for 自旋,再走槽点有值的 put 流程,这里就是自旋 + CAS 的结合。

  3. 当前槽点有值时,会锁住当前槽点。put 时,如果当前槽点有值,也就是 key 的 hash 冲突的情况,此时槽点上可能是链表或红黑树,通过锁住槽点,来保证同一时刻只会有一个线程能对槽点进行修改。

  4. 红黑树旋转时,锁住红黑树的根节点,保证同一时刻,当前红黑树只能被一个线程旋转

用户头像

zarmnosaj

关注

靡不有初,鲜克有终 2020.02.06 加入

成都后端混子

评论

发布
暂无评论
ConcurrentHashMap 源码分析-初始化_6月月更_zarmnosaj_InfoQ写作社区