龙空技术网

ConcurrentHashMap源码分析 - 初始化数组

渐流涟 88

前言:

而今同学们对“初始化数组为什么不能用n”大概比较关怀,姐妹们都想要剖析一些“初始化数组为什么不能用n”的相关文章。那么小编同时在网摘上搜集了一些对于“初始化数组为什么不能用n””的相关内容,希望大家能喜欢,小伙伴们一起来学习一下吧!

1.initTable方法

ConcurrentHashMap在插入数据时,如果数组为空,则会先进行初始化操作

初始化调用initTable方法来完成,方法中使用了CAS机制和双层检查机制来解决线程安全问题

初始化的时候会判断sizeCtl的值,保证只能有一个线程执行初始化操作,其他的线程使用yield的方式来自旋等待

    private final Node<K,V>[] initTable() {        Node<K,V>[] tab; int sc;		//创建tab并使用table赋值		//判断tab是否已经初始化,有的话直接跳过,返回tab即可       	//tab没有初始化的话则需要则进入循环中进行初始化操作,直到tab初始化完成        while ((tab = table) == null || tab.length == 0) {			//使用sizeCtl给sc赋值,在没有初始化的情况下sizeCtl默认为0,进入else里面,else里面需要将sc的值修改为小于0,防止其他线程进入			//其他线程如果sc小于0,则线程会进行让步,进入自旋状态			//只有第一个线程初始化成功后,则跳出循环            if ((sc = sizeCtl) < 0)                Thread.yield();						//else选项,第一个线程进入是sc为0,进入该分支			//首先通过CAS对sc的值进行判断并尝试修改为-1,如果成功则进入分支			//如果有两个线程同时跑到此处,因为compareAndSetInt是原子性的,只能有一个线程修改成功			//修改失败的线程继续执行循环,第二次循环时sc已经置为-1了,线程进入自旋等待            else if (U.compareAndSetInt(this, SIZECTL, sc, -1)) {                try {					//双层检查机制,第一个线程进来初始化时正常执行					//初始化线程完成之后,退出该逻辑,在sc > 0的情况下,自旋线程可能会进入到此处					//进入后再次判断table是否已经初始化,此时初始化已经完成,跳过后面的逻辑                    if ((tab = table) == null || tab.length == 0) {						//获取初始容量,如果在构造方法中没有传入初始容量,则默认为DEFAULT_CAPACITY的值16                        int n = (sc > 0) ? sc : DEFAULT_CAPACITY;                        						//使用n创建Node数组                        Node<K,V>[] nt = (Node<K,V>[])new Node<?,?>[n];						//将创建的数组赋值为table                        table = tab = nt;						//计算sc为容量的3/4                        sc = n - (n >>> 2);                    }                } finally {					//将sc的值赋值给sizeCtl                    sizeCtl = sc;                }                break;            }        }        return tab;    }

标签: #初始化数组为什么不能用n