专业的JAVA编程教程与资源

网站首页 > java教程 正文

ConcurrentHashMap源码分析 - 初始化数组

temp10 2024-10-23 15:04:28 java教程 11 ℃ 0 评论

1.initTable方法

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

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

ConcurrentHashMap源码分析 - 初始化数组

初始化的时候会判断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;
    }

本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表