IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> JDK1.8 CourrentHashMap中addCount的小bug -> 正文阅读

[Java知识库]JDK1.8 CourrentHashMap中addCount的小bug

1. 扩容戳

每次扩容前,会调用resizeStamp函数以table容量为种子生成一个唯一的扩容戳。

static final int resizeStamp(int n) {
    return Integer.numberOfLeadingZeros(n) | (1 << (RESIZE_STAMP_BITS - 1));
  	// Integer.numberOfLeadingZeros 表示参数n的二进制表示,从左边开始算起,连续为0的个数
}

在扩容时,sizeCtl变量会用来表示扩容状态,高16位保存着扩容戳,低16位保存着并发扩容的线程数

2.addCount方法分析

在putVal方法中,插入节点完毕后,调用addCount(1L, binCount),更新Map元素计数,其中含有扩容判断。Map元素总数超过阈值(0.75* table容量)则触发扩容。

扩容函数:

addCount()中扩容部分:

?<1>处代码分析:检验当前线程是否可以参与扩容

  1. (sc >>> RESIZE_STAMP_SHIFT) != rs

    如果不相等,说明table容量已经变化,扩容以及完成,此时sizeCtl为扩容阈值

  2. sc == rs + 1 (此处为jdk1.8中的bug,已在jdk12以后修改)

    用于判读扩容结束,(rs << 16) + 1表示扩容结束,+2表示第一个线程进行扩容,若第一个线程扩容结束,会讲rs进行-1操作,此时sc == (rs << 16) +1

  3. sc == rs + MAX_RESIZERS (此处为jdk1.8中的bug,已在jdk12以后修改)

    此初应该是sc == (rs << 16) + MAX_RESIZERS ,MAX_RESIZERS默认为( 1<<16 ) - 1

    扩容线程数量已达到最大值

  4. (nt = nextTable) == null

    此处表示nextTable还未完成初始化(只能有一个线程去完成)

  5. transferIndex <= 0

    表示需要迁移的桶已经被并发扩容线程瓜分完毕,无法继续帮助扩容

此处addCount以及helpTransfer中 int rs = resizeStamp(n); 赋值逻辑存在问题(JDK1.8中),rs获取的扩容戳为低16位数据,与sc进行比较时,需要左移16位才能正确判断之后的 sc == rs + 1以及 sc == rs + MAX_RESIZERS
该Bug已在JDK12中被修复,见下面的链接。
[Bug ID: JDK-8214427 probable bug in logic of ConcurrentHashMap.addCount()]
在stackoverflow中也找到了该问题的讨论。
stackoverflow.com

这个bug是2020年才在JDK12中修改的,对程序影响估计也很小,国内这么多公司都在用JDK1.8,也没出现什么大问题,因此也会很久没有修改过来吧,但是大家阅读源码过程中还是应该去看新版本JDK12以上的,此处已经修改过来了。

若要复现此bug,需要做到并发扩容线程达到最大值时进行判断,很难去模拟该情况(电脑跑到那么多线程确实也很离谱,默认情况下MAX_RESIZERS为 (1<< 16)-1 ),以及判断当前扩容是否结束,很难模拟一个线程刚好允许到此处,刚好遇到了sc == (rs << 16) +1 的情况。

JDK8中源码如下:

addCount方法:

    
//检查是否要进行扩容,插入元素在putVal方法中总会进行检查
        if (check >= 0) {
            Node<K,V>[] tab, nt; int n, sc;
          	//此处sizeCtl表示的是扩容阈值,s的值为之前sumCount()计算得到的,表示当前总KV对个数
            while (s >= (long)(sc = sizeCtl) && (tab = table) != null &&  
                   (n = tab.length) < MAXIMUM_CAPACITY) {  
              	//此处rs的赋值存在bug
              	int rs = resizeStamp(n);
                //int rs = resizeStamp(n) << RESIZE_STAMP_SHIFT; //生成当前容量n对应的扩容戳
                if (sc < 0) { // 并发扩容已经开始进行
                  // <1> flag
                    if ((sc >>> RESIZE_STAMP_SHIFT) != rs || sc == rs + 1 ||
                        sc == rs + MAX_RESIZERS || (nt = nextTable) == null ||
                        transferIndex <= 0) // 检验当前线程是否可以参与扩容
                        break;
                  	// 通过检验,update sizectl -> sizectl+1,表示多了一个线程参与扩容
                    if (U.compareAndSwapInt(this, SIZECTL, sc, sc + 1))
                        transfer(tab, nt);
                }
              	// sc >= 0 说明当前容器还未进行并发扩容,运行此处的线程是第一个扩容线程
                else if (U.compareAndSwapInt(this, SIZECTL, sc,
                                             (rs << RESIZE_STAMP_SHIFT) + 2)) // rs << 16 + 1表示扩容结束
                  														// 此处+2 表示第一个线程进行扩容,更新sizeCtl变量从而修改扩容状态
                    transfer(tab, null);
                s = sumCount();
            }
        }
  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2021-10-04 12:43:31  更:2021-10-04 12:44:40 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/23 19:12:31-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码