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 的相关文章

  • Doris--基础--06--设置内存

    Doris 基础 06 设置内存 1 问题 内存不够时 查询可能会出现 Memory limit exceeded 这是因为doris对每个用户默认设置内存限制为 4g 2 设置内存 2 1 查看当前内存 SHOW VARIABLES LI

随机推荐

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

    一 知识总览 二 进程调度的时机 需要进行进程调度与切换的情况 不能进行进程调度与切换的情况 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