JUC源码分析2-原子变量-AtomicIntegerArray/AtomicLongArray/AtomicReferenceArray

2023-10-31

JUC针对数组元素的原子封装,先看AtomicIntegerArray。

private static final Unsafe unsafe = Unsafe.getUnsafe();
//arrayBaseOffset获取数组首个元素地址偏移
private static final int base = unsafe.arrayBaseOffset(int[].class);
//shift就是数组元素的偏移量
private static final int shift;
private final int[] array;

static {
//scale数组元素的增量偏移
    int scale = unsafe.arrayIndexScale(int[].class);
//对于int型数组,scale是4,用二进制&操作判断是否是2的倍数,这个判断nb    
    if ((scale & (scale - 1)) != 0)
        throw new Error("data type scale not a power of two");
//这里是处理int型的偏移量,shift是2        
    shift = 31 - Integer.numberOfLeadingZeros(scale);
}
/**
计算数组中元素的地址
*/
private long checkedByteOffset(int i) {
    if (i < 0 || i >= array.length)
        throw new IndexOutOfBoundsException("index " + i);

    return byteOffset(i);
}
/**
计算数组中元素的地址,首地址偏移+每个元素的偏移,采用了移位操作,没想过还可以用,佩服
*/
private static long byteOffset(int i) {
    return ((long) i << shift) + base;
}

为了说明unsafe对数组的操作,举个栗子:

public class UnsafeTest {

    public static void main(String[] args) throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
        Field field = Unsafe.class.getDeclaredField("theUnsafe");
        field.setAccessible(true);
        Unsafe unsafe = (Unsafe) field.get(null);
        
        int[] a = new int[]{1,2,5};
        //int[]首个元素的偏移
        int arrayBaseOffset = unsafe.arrayBaseOffset(a.getClass());
        //数组中元素的增量偏移
        int arrayIndexScale = unsafe.arrayIndexScale(a.getClass());
        System.out.println(arrayBaseOffset);
        System.out.println(arrayIndexScale);
        //通过首地址的偏移+增量偏移,获取数组元素值
        System.out.println(unsafe.getIntVolatile(a, arrayBaseOffset+arrayIndexScale));
    }
}

源码里面对于增量偏移使用了移位操作,这步处理没看源码前还真是没想到


假设数组首地址14,int型,每个4个字节,所以取第0个元素地址就是,14+(0*4),第2个元素14+(1*4),第i个地址为14+(i*4),采用移位的话就是(4的2进制是2)14+(i<<2),对于AtomicLongArray取数组元素就是首地址+(i<<3)。对于AtomicInteger取元素地址就是base+(index<<scale)。

看下AtomicIntegerArray的构造函数

    public AtomicIntegerArray(int[] array) {
        // Visibility guaranteed by final field guarantees
        this.array = array.clone();
    }
这里有个说明,采用final来保证数组的可见性,看到这里的时候,郁闷,以前final的使用,重来没想过什么可见性问题,直接用就是了,只好百度,http://www.infoq.com/cn/articles/java-memory-model-6/这里对于final的可见性有详细说明,也是通过加内存屏障来实现。最重要一句: JSR-133专家组增强了final的语义。通过为final域增加写和读重排序规则,可以为java程序员提供初始化安全保证:只要对象是正确构造的(被构造对象的引用在构造函数中没有“逸出”),那么不需要使用同步(指lock和volatile的使用),就可以保证任意线程都能看到这个final域在构造函数中被初始化之后的值。

看下AtomicIntegerArray的一些方法:

public final int get(int i) {
    return getRaw(checkedByteOffset(i));
}
/**
通过unsafe.getIntVolatile这个保证volatile语义,
感觉意思应该是可以当场volatile变量使用
 */
private int getRaw(long offset) {
    return unsafe.getIntVolatile(array, offset);
}

/**
unsafe.putIntVolatile保证volatile语义
 */
public final void set(int i, int newValue) {
    unsafe.putIntVolatile(array, checkedByteOffset(i), newValue);
}

/**
unsafe.putOrderedInt这个前面一文提过,
去掉了storeLoad内存屏障,只有storestore屏障,只保证修改成功,不保证修改后其他处理器立即就可以看到
 */
public final void lazySet(int i, int newValue) {
    unsafe.putOrderedInt(array, checkedByteOffset(i), newValue);
}
/**
这里是通过while循环来实现最终设置成功
 */
public final int getAndSet(int i, int newValue) {
    long offset = checkedByteOffset(i);
    while (true) {
        int current = getRaw(offset);
        if (compareAndSetRaw(offset, current, newValue))
            return current;
    }
}
/**
对于数组的cas,只是多了个先取地址的操作,其他还是底层的unsafe
 */
public final boolean compareAndSet(int i, int expect, int update) {
    return compareAndSetRaw(checkedByteOffset(i), expect, update);
}
private boolean compareAndSetRaw(long offset, int expect, int update) {
    return unsafe.compareAndSwapInt(array, offset, expect, update);
}

跟之前的AtomicInteger没太多区别,只是需要对于传入的i需要先判断在数组length范围内,再转换成内存地址。类中其他方法类同。

AtomicLongArray跟AtomicIntegerArray类同。

AtomicReferenceArray多了个arrayFieldOffset,用来在从stream读数组设置到array内存偏移地址。


参考:

http://www.infoq.com/cn/articles/java-memory-model-6/ final关键字的可见性

http://brokendreams.iteye.com/blog/2250117

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

JUC源码分析2-原子变量-AtomicIntegerArray/AtomicLongArray/AtomicReferenceArray 的相关文章

  • Cpu运作原理与机制,那么CPU如何跑的更快?

    前言 代码都是由 CPU 跑起来的 我们代码写的好与坏就决定了 CPU 的执行效率 特别是在编写计算密集型的程序 更要注重 CPU 的执行效率 否则将会大大影响系统性能 CPU 内部嵌入了 CPU Cache 高速缓存 它的存储容量很小 但
  • BatchNorm原理以及PyTorch实现

    BatchNorm算法 简单来说BatchNorm对输入的特征按照通道计算期望和方差 第1和第2个公式 并标准化 第3个公式 减去均值 再除方差 变为均值0 方差1 但这会降低网络的表达能力 因此 BN在标准化后还要进行缩放平移 也就是可学
  • JUC并发编程之ReentrantLock

    1 非公平锁实现原理 加锁解锁流程 构造器默认实现的是非公平锁 public ReentrantLock sync new NonfairSync NonfairSync 继承 Sync Sync 继承 AbstractQueuedSync
  • 语音识别之HMM算法及其源码

    基础 1 了解HMM算法 http www cnblogs com pangxiaodong archive 2011 10 17 2214542 html 2 再次加深印象 http blog csdn net likelet artic
  • RecyclerView中item布局的"match_parent"属性失效--LayoutInflate的深入了解

    用recyclerview 给item布局使用了match parent属性 运行后不起作用 查了下 是在onCreateViewHolder中加载布局时候出了问题 一开始用的View Inflate方法 查看源码后 发现View infl
  • 基于java springboot vue仓库管理系统源码(毕设)

    开发环境及工具 大于Jdk1 8 大于mysql5 5 idea eclipse vscode webstorm 技术说明 Springboot mybatis vue elementui 代码注释齐全 没有多余代码 适合学习 毕设 二次开
  • 【排错日记】PageHelper插件的默认分页参数

    现象 没有写如下代码 执行的结果却被分页显示了 PageHelper startPage listParam getPageNum listParam getPageSize 源码分析 调用方法判断是否需要进行分页 如果不需要 直接返回结果
  • 源码分析shiro认证授权流程

    1 shiro介绍 Apache Shiro是一个强大易用的Java安全框架 提供了认证 授权 加密和会话管理等功能 认证 用户身份识别 常被称为用户 登录 授权 访问控制 密码加密 保护或隐藏数据防止被偷窥 会话管理 每用户相关的时间敏感
  • [Hyperf]源码阅读:验证器验证规则

    hyperf validation src Concerns ValidatesAttributes php
  • 迁移学习源码全注释 - 《Tensorflow 实战 Google 深度学习框架》源码注释

    学习迁移学习源码 做了完全版本注释 以做记录 coding utf 8 Created on Mon Dec 25 12 30 25 2017 需要提前下载训练好的 Inception V3模型 以及对应的数据文件 author Admin
  • Android 字体国际化适配方法以及源码解析

    起源 由于我们公司的app 支持多国语言 所以就导致了 同样的文案 但是长度不同 就会出现适配的问题 因为 中文 是 字表义 外文是 音表义 今天就用8 0新特新来解决这个问题 适配前是这样的 在固定的宽高就会出现适配的问题 在之前博客中也
  • Caffe源码(十一):io.cpp 分析

    目录 目录 简单介绍 主要函数 ReadProtoFromTextFile 函数 WriteProtoToTextFile 函数 ReadProtoFromBinaryFile 函数 WriteProtoToBinaryFile 函数 Re
  • 线程安全分析

    1 成员变量和静态变量是否线程安全 如果它们没有被共享 则线程安全 如果它们被共享了 根据它们的状态是否能够改变 又分两种情况 如果只有读操作 则线程安全 如果有读写操作 则这段代码是临界区 需要考虑线程安全 2 局部变量是否线程安全 局部
  • 【JUC并发编程】CopyOnWrite容器详解

    JUC并发编程 CopyOnWrite容器详解 文章目录 JUC并发编程 CopyOnWrite容器详解 一 什么是CopyOnWrite容器 二 CopyOnWriteArrayList 三 CopyOnWrite的业务中实现 一 什么是
  • QGis二次开发 -- 源码编译终极篇

    由于是开源软件 QGis版本迭代比较快 在保持long term release版本的基础上 每个月都会有一个monthly release的新版本发布 源码工程变化快速 给想要上手编译开发的新人朋友带来了一些困惑 我之前分别写过QGis1
  • Spring源码深度解析:文章目录

    文章目录 序号 内容 链接地址 1 一 Spring整体架构和源码环境搭建 https blog csdn net wts563540 article details 126686645 2 二 手写模拟Spring https blog
  • JUC并发编程共享模型之管程(三)(中)

    4 5Monitor概念 Java 对象头 以 32 位虚拟机为例 在32位虚拟机中 1个机器码等于4字节 也就是32bit 在64位虚拟机中 1个机器码是8个字节 也就是64bit 普通对象 数组对象 其中Mark Word 结构为 最后
  • 万文详解JUC(超详细)

    生命无罪 健康万岁 我是laity 我曾七次鄙视自己的灵魂 第一次 当它本可进取时 却故作谦卑 第二次 当它在空虚时 用爱欲来填充 第三次 在困难和容易之间 它选择了容易 第四次 它犯了错 却借由别人也会犯错来宽慰自己 第五次 它自由软弱
  • 病案管理的定义、流程及应用分析

    病案管理是指针对病人的基本信息 病历 就诊记录等进行收集 整理 存储 分析和应用的一项管理工作 它在医院 医疗机构和医疗行业中具有重要的作用 能够提高医疗服务的质量 效率和安全性 本文将就病案管理的定义 流程以及其在医疗健康领域中的应用进行
  • 医院绩效核算系统源码,java语言开发

    医院绩效考核系统全套源码 医院绩效核算系统源码 java语言开发 医院绩效考核系统可根据工作绩效考核管理规定 配置相应的绩效考核模型 从工作量统计 核算维度 核算权重三方面计算工作绩效 利用数据处理和数据分析的支撑作用 实现对工作量统计和绩

随机推荐

  • 操作系统学习(十二)进程调度的时机、切换与过程、方式

    一 知识总览 二 进程调度的时机 需要进行进程调度与切换的情况 不能进行进程调度与切换的情况 1 中断 2 临界区 3 原子操作 临界资源 一段时间内只允许一个进程使用的资源 各个进程需要互斥地访问临界资源 临界区 访问临界资源的那段代码
  • Python——coco格式图像分割数据集转mask

    文章目录 单张coco转mask并显示 批量coco转mask 目前很多深度学习框架中的图像分割套件都使用image mask格式的标签数据 所以为了方便使用写了该脚本进行转换 单张coco转mask并显示 convert coco2mas
  • pycharm matplotlib.pyplot 绘图一闪而过解决办法

    今天在写python作业的时候发现用python绘图使用show方法出现了一点问题 什么问题呢 如题 绘制的窗口一闪而过 不留痕迹 怎么解决 问百度而得之 发现很多都是遇到不识别turtle的关键字 和我遇到的问题都不一样 这就很麻烦 然后
  • 7.Oracle19c RAC集群安装部署

    1 Oracle 19c RAC For Linux安装部署 https edu csdn net course detail 35792 2 Oracle数据库 底层原理解析 解析oracle数据库内部实现 详细讲解了Oracle数据库内
  • Android 状态栏处理三种方式

    记录三种对状态栏处理的方式 只对android 4 4版本以上有效果 第一种 全屏显示 屏蔽掉状态栏 一般是应用查看大图片或者闪屏界面应用 很简单 直接定义style
  • 使用uView根据权限动态配置uni-app中的tabBar

    转载一 动态配置权限 转载二 uniapp页面速成提效工具 uniapp uview ui 可视化 完全自由拖拽 一键生成flex代码网站 http aicode shagua wiki uni index html 十大特性 1 可视化
  • 【halcon】亚像素轮廓XLD

    XLD eXtended Line Descriptions XLD其实就是指的亚像素轮廓 如何理解亚像素 上一篇 halcon入门小技巧 提到的 threshold Image Region 128 255 这个呢 是给了一个灰度的范围
  • Spring Cloud微服务治理框架深度解析

    在学习一个技术之前 首先我们要了解它是做什么的 我们为什么要用它 不然看再多资料都理解不了 因此我们先来讲解下Spring Cloud Spring Cloud是一套微服务治理框架 几乎考虑到了微服务治理的方方面面 那么接下来具体说下 Sp
  • 【爬虫-反爬虫】系列一:反爬虫之签名(6)

    反爬虫之签名 6 本讲介绍的是一种比较麻烦的反爬虫策略 请求签名 请求签名 请求签名指在请求url中增加一个sign字段 通常取值为自定义字段的md5校验码 前面介绍的反爬虫策略基本上都有规律可寻 但签名很让人头疼 因为必须硬手段破解 也就
  • 【Flutter 2-11】Flutter手把手教程UI布局和Widget——列表ListView

    作者 弗拉德 来源 弗拉德 公众号 fulade me ListView ListView是在移动端非常常见的控件 在大多数的展示场景中都离不开ListView 在Flutter中对ListView的封装也非常好 简单几行代码就可以满足我们
  • redis基本操作

    redis是Remote Dictionary Service的简称 也就是远程字典服务 redis 是内存数据库 KV数据库 数据结构数据库 编译安装登录这些操作在之前已经介绍过 这里不再赘述 redis存储结构 redis有多种存储结构
  • 基于Keras的深度学习防止过拟合的策略:学习率、正则化、dropout、权重初始化、Early Stopping等

    本博客致力于记录心得体会 用简单通俗的语言分享经验和代码 记录我们学习的点滴 记录我们成长的过程 Keras包 Keras是深度学习中非常好用的一个工具包 我在刚开始接触深度学习的时候 尝试过用pytorch TensorFlow 但最后我
  • [Python从零到壹] 四十五.图像增强及运算篇之图像灰度非线性变换详解

    欢迎大家来到 Python从零到壹 在这里我将分享约200篇Python系列文章 带大家一起去学习和玩耍 看看Python这个有趣的世界 所有文章都将结合案例 代码和作者的经验讲解 真心想把自己近十年的编程经验分享给大家 希望对您有所帮助
  • 何为依赖注入(DI)?

    何为依赖注入 DEPENDENCY INJECTION 平常在写代码的时候 经常会在一个类中写出这样的一段代码 以 PHP 代码展示
  • MobaXterm自动断开连接设置

    场景 使用MobaXterm工具通过SSH连接Linux服务器 如果一段时间没有操作 MobaXterm会把连接自动断开 这个设定很是不方便 通过更改下面的设置可以使SSH保持长连接 不会自动断开 点击设置 把 SSH keepalive
  • 社会共治大命题下,区块链技术究竟有何魔力?

    健康码互认 绿色行为 碳交易 诚信积分 链上社区 区块链在社会治理上发挥的作用远超你想象 文 Azuma 运营 盖遥 编辑 郝方舟 出品 Odaily星球日报 ID o daily 转眼间 2020 年已接近尾声 疫情 无疑这魔幻的一年里最
  • 【网络结构设计】11、E-LAN

    文章目录 一 背景 二 方法 2 1 网络设计策略 2 2 Partial Residual Networks 2 3 Cross Stage Partial Networks 2 4 Efficient Layer Aggregation
  • 在 C++ 代码中调用 NumPy

    要在 C 代码中调用 NumPy 可以使用 Boost Python 库 为了这样做 需要安装 Boost 开发库和 NumPy 并使用 BOOST PYTHON MODULE 宏定义来创建一个 Python 模块 以下是一个简单的示例 演
  • 【知识学习】MySQL:数据库知识手册笔记(下)

    MySQL 数据库知识手册笔记 下 目录 1 锁 1 1 锁的分类 具体异同还需思考 1 2 事务隔离级别与锁的关系 1 3 什么是死锁 如何解决死锁 1 3 1 是什么 1 3 2 怎么办 1 4 什么是乐观锁和悲观锁 如何实现 2 常用
  • JUC源码分析2-原子变量-AtomicIntegerArray/AtomicLongArray/AtomicReferenceArray

    JUC针对数组元素的原子封装 先看AtomicIntegerArray private static final Unsafe unsafe Unsafe getUnsafe arrayBaseOffset获取数组首个元素地址偏移 priv