LongAdder 和 Striped64 基础学习

cs,表示 Cell 数组的引用;b,表示获取的 base 值,类似于 AtomicLong 中全局变量的 value 值,在没有竞争的情况下数据直接累加到 base 上,或者扩容时,也需要将数据写入到 base 上;v,表示期望值;m,表示 Cell 数组的长度;a,表示 当前线程命中的 cell 表格。

public void add(long x) {Cell[] cs; long b, v; int m; Cell c;// 首次首线程 ((cs = cells) != null) 一定是false;此时走 casBase(long cmp, long val) 方法,当且仅当 cas 失败的时候,才会走到 if 中。if ((cs = cells) != null || !casBase(b = base, b + x)) {// getProbe(); 的返回的是线程中 threadLocalRandomProbe 字段,它是通过一个随机数字段,对于一个特定的线程这个值是固定的(除非刻意修改它)int index = getProbe();// true,无竞争;false,表示竞争激烈,多个线程 hash 到同一个 Cell,可能需要扩容boolean uncontended = true;// 条件一:Cell[] 为 null// 条件二:一般不会出现// 条件三:当前线程所在的 cell 为空,说明当前线程还没有更新过 Cell,应初始化一个 Cell// 条件四:更新当前线程所在的 cell 失败,说明现在竞争很激烈,多个线程 hash 到了同一个 Cell,应扩容if (cs == null || (m = cs.length - 1) < 0 ||(c = cs[index & m]) == null ||!(uncontended = c.cas(v = c.value, v + x)))// longAccumulate(long x, LongBinaryOperator fn, boolean wasUncontended, int index)longAccumulate(x, null, uncontended, index); // x,一般默认都是1;fn,默认是 null,uncontended,如果是第四个条件,那就是false。}
}
final void longAccumulate(long x, LongBinaryOperator fn,boolean wasUncontended, int index) {if (index == 0) {// 如果 index 是0,那就强制为当前线程初始化ThreadLocalRandom.current(); // force initialization// getProbe(); 获取当前线程的 hash 值index = getProbe();wasUncontended = true; // true,表示无竞争,重新计算了当前线程的 hash 值后认为此次不算是一次竞争,因为都还没有进行初始化,肯定还不存在竞争激烈}// collide 表示扩容意向,false 表示一定不会扩容, true 表示可能会扩容for (boolean collide = false;;) {       // True if last slot nonemptyCell[] cs; Cell c; int n; long v;// 第一种情况:Cell[] 数组已经被初始化了if ((cs = cells) != null && (n = cs.length) > 0) {// 当前前程的 hash 值运算后映射得到的 Cell 单元为 null,说明该 cell 没有被使用过if ((c = cs[(n - 1) & index]) == null) {// cellsBusy,初始化 cells,或者扩容 cells 需要获取锁。0,表示无锁状态。1,表示其他线程已经持有了锁。if (cellsBusy == 0) {       // Try to attach new CellCell r = new Cell(x);   // Optimistically create// casCellsBusy(),通过 cas 操作修改 cellsBusy 的值,cas 成功代表获取锁,返回 true// 尝试加锁,成功后 cellsBusy = 1if (cellsBusy == 0 && casCellsBusy()) {try {               // Recheck under lockCell[] rs; int m, j;// 在有锁的情况下再检测一遍之前的判断if ((rs = cells) != null &&(m = rs.length) > 0 &&rs[j = (m - 1) & index] == null) {rs[j] = r; // 将 Cell 添加到 Cell[] 数组中break;}} finally {cellsBusy = 0;}continue;           // Slot is now non-empty}}collide = false;}// 这个分支,表示我竞争失败了,将 wasUncontended = true; 执行 index = advanceProbe(index); 重新竞争else if (!wasUncontended)       // CAS already known to failwasUncontended = true;      // Continue after rehashelse if (c.cas(v = c.value,(fn == null) ? v + x : fn.applyAsLong(v, x)))break;// NCPU,当前计算机 CPU 的个数,Cell 数组扩容时会用到else if (n >= NCPU || cells != cs) // 如果 n = cs.length 大于了 CPU 的数量或者 cells 和我们的 cs 不是同一个,那就别扩容了collide = false;            // At max size or staleelse if (!collide)collide = true;else if (cellsBusy == 0 && casCellsBusy()) {try {// 当前的 cells 数组和最先赋值的cs是同一个,代表没有被其他线程扩容过if (cells == cs)        // Expand table unless stalecells = Arrays.copyOf(cs, n << 1); // 按位左移1位,相当于扩大为原来大小的两倍,扩容后再将原先的数组元素拷贝到新数组中} finally {cellsBusy = 0; // 释放锁}collide = false;continue;                   // Retry with expanded table}// advanceProbe(index); 重置当前线程的 hash 值index = advanceProbe(index);}// 第二种情况:Cell[] 数组还没有进行初始化,尝试对它加锁,并首次 Cell[] 数组初始化else if (cellsBusy == 0 && cells == cs && casCellsBusy()) {try {                           // Initialize tableif (cells == cs) {Cell[] rs = new Cell[2];rs[index & 1] = new Cell(x);cells = rs;break;}} finally {cellsBusy = 0;}}// 第三种情况:Cell[] 数组正在进行初始化的时候(第二步还没有完成,其他线程来了),则尝试直接在基数 base 上进行累加操作,兜底的操作// Fall back on using baseelse if (casBase(v = base,(fn == null) ? v + x : fn.applyAsLong(v, x)))break;}
}

double check

static final int getProbe() {return (int) THREAD_PROBE.get(Thread.currentThread());}

看下里面的 THREAD_PROBE,在 jdk8 中用的是 UNSAFE 类,这里是 MethodHandles.lookup()。

MethodHandles.Lookup l = MethodHandles.lookup();
THREAD_PROBE = l.findVarHandle(Thread.class, "threadLocalRandomProbe", int.class);

左移(<<)规则:符号位不变,高位溢出截断,低位补零。比如 -1 << 2 = -4 (为方便讲解,图示的补码为-1)
左移位运算
位运算规则:

Java数值运算过程中都是先将十进制转换为二进制然后再进行运算,再把二进制数据转换为十进制展现给用户。二进制运算规则如下:
1、对于有符号的而言,最高位为符号位,0表示正数,1表示负数。
2、正数的原码,反码和补码都一样,三码合一。
3、负数的反码:符号位保持不限,其他位取反,负数的补码:补码 + 1。
4、0的反码和补码都是0。

下面以 -1 为例子展示原码、反码和补码的转换关系(以int数据类型为例,int类型在Java中占4字节):
原码、反码和补码的关系

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/296854.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

32-2 APP渗透 - 移动APP架构

前言 app渗透和web渗透最大的区别就是抓包不一样 一、客户端: 反编译: 静态分析的基础手段,将可执行文件转换回高级编程语言源代码的过程。可用于了解应用的内部实现细节,进行漏洞挖掘和算法分析等。调试: 排查软件错误的一种手段,用于分析应用内部原理和行为。篡改/重打…

Unity | Shader基础知识(第十一集:什么是Normal Map法线贴图)

目录 前言 一、图片是否有法线贴图的视觉区别 二、有视觉区别的原因 三、法线贴图的作用 四、信息是如何存进去的 五、自己写一个Shader用到法线贴图 六、注意事项 七、作者的话 前言 本小节会给大家解释&#xff0c;什么是法线贴图&#xff1f;为什么法线贴图会产生深…

GPT4不限制使用次数了!GPT5即将推出了!

今天登录到ChatGPT Plus账户&#xff0c;出现了如下提示&#xff1a; 已经没有了数量和时间限制的提示。 更改前&#xff1a;每 3 小时限制 40 次&#xff08;团队计划为 100 次&#xff09;&#xff1b;更改后&#xff1a;可能会应用使用限制。 GPT-4放开限制 身边订阅了Ch…

C++多线程:单例模式与共享数据安全(七)

1、单例设计模式 单例设计模式&#xff0c;使用的频率比较高&#xff0c;整个项目中某个特殊的类对象只能创建一个 并且该类只对外暴露一个public方法用来获得这个对象。 单例设计模式又分懒汉式和饿汉式&#xff0c;同时对于懒汉式在多线程并发的情况下存在线程安全问题 饿汉…

【原创】基于分位数回归的卷积长短期结合注意力机制的神经网络(CNN-QRLSTM-Attention)回归预测的MATLAB实现

基于分位数回归的卷积长短期结合注意力机制的神经网络&#xff08;CNN-QRLSTM-Attention&#xff09;是一种用于时间序列数据预测的深度学习模型。该模型结合了卷积神经网络&#xff08;CNN&#xff09;、长短期记忆网络&#xff08;LSTM&#xff09;和注意力机制&#xff08;A…

C语言实现通讯录(从0-1的项目)

一、前言 1、实现通讯录首先我们要了解并懂得如何通过C语言来完成有关顺序表的实现 2、需要了解的内容&#xff1a;如何使用顺序表结构实现增、删、改、查等操作 二、顺序表的认识和实现 1、什么是顺序表 最基础的数据结构就是数组。 顺序表则是线性表的一种&#xff0c;…

图片改大小尺寸怎么改?几个修改图片尺寸的方法

日常生活和工作中&#xff0c;图片的大小和尺寸对于我们的工作和生活都至关重要&#xff0c;因此我们经常需要调整图片的大小。我们都知道压缩图是一款功能强大的图片在线处理工具&#xff0c;那么用它怎么调整图片大小呢&#xff1f;下面就让我们一起来看一下具体的操作步骤。…

基于Spring Boot的在线考试系统

开发语言&#xff1a;Java 框架&#xff1a;springboot JDK版本&#xff1a;JDK1.8 服务器&#xff1a;tomcat7 数据库&#xff1a;mysql 5.7&#xff08;一定要5.7版本&#xff09; 数据库工具&#xff1a;Navicat11 开发软件&#xff1a;eclipse/myeclipse/idea Maven…

实时渲染 -- 材质(Materials)

一、自然界中的材质 首先了解下自然界中的材质 如上这幅图&#xff0c;不同的物体、场景、组合&#xff0c;会让我们看到不同的效果。 我们通常认为物体由其表面定义&#xff0c;表面是物体和其他物体或周围介质之间的边界面。但是物体内部的材质也会影响光照效果。我们目前只…

微服务(基础篇-008-es、kibana安装)

目录 05-初识ES-安装es_哔哩哔哩_bilibilihttps://www.bilibili.com/video/BV1LQ4y127n4?p81&vd_source60a35a11f813c6dff0b76089e5e138cc 1.部署单点es 1.1.创建网络 1.2.加载镜像 1.3.运行 2.部署kibana 2.1.部署 2.2.DevTools 3.安装IK分词器 3.1.在线安装ik…

【IP组播】PIM-SM的RP、RPF校验

目录 一&#xff1a;PIM-SM的RP 原理概述 实验目的 实验内容 实验拓扑 1.基本配置 2.配置IGP 3.配置PIM-SM和静态RP 4.配置动态RP 5.配置Anycast RP 二&#xff1a; RPF校验 原理概述 实验目的 实验内容 实验拓扑 1.基本配置 2.配置IGP 3.配置PIM-DM 4.RPF校…

【洛谷 P8695】[蓝桥杯 2019 国 AC] 轨道炮 题解(映射+模拟+暴力枚举+桶排序)

[蓝桥杯 2019 国 AC] 轨道炮 题目描述 小明在玩一款战争游戏。地图上一共有 N N N 个敌方单位&#xff0c;可以看作 2D 平面上的点。其中第 i i i 个单位在 0 0 0 时刻的位置是 ( X i , Y i ) (X_i, Y_i) (Xi​,Yi​)&#xff0c;方向是 D i D_i Di​ (上下左右之一, 用…

基于Spring Boot的餐厅点餐系统

基于Spring Boot的餐厅点餐系统 开发语言&#xff1a;Java框架&#xff1a;springbootJDK版本&#xff1a;JDK1.8数据库工具&#xff1a;Navicat11开发软件&#xff1a;eclipse/myeclipse/ideaMaven包&#xff1a;Maven3.3.9 部分系统展示 管理员登录界面 用户注册登录界面 …

股权激励和期权激励对比辨析

文章目录 概念定义 收益方式 风险评估 应用和分析 股权激励和期权激励&#xff0c;两者的区别是什么&#xff0c;本文就来梳理对比一下。 概念定义 股权激励&#xff0c;是指上市公司以本公司股票为标的&#xff0c;对其董事、高级管理人员及其他员工进行的长期性激励。取得…

JVM专题——类文件加载

本文部分内容节选自Java Guide和《深入理解Java虚拟机》, Java Guide地址: https://javaguide.cn/java/jvm/class-loading-process.html &#x1f680; 基础&#xff08;上&#xff09; → &#x1f680; 基础&#xff08;中&#xff09; → &#x1f680;基础&#xff08;下&a…

C++从入门到精通——入门知识

1. C关键字(C98) C总计63个关键字&#xff0c;C语言32个关键字 2. 命名空间 在C/C中&#xff0c;变量、函数和后面要学到的类都是大量存在的&#xff0c;这些变量、函数和类的名称都将存在于全局作用域中&#xff0c;可能会导致很多冲突。使用命名空间的目的就是对标识符的名…

ST表---算法

相当于二分的思想&#xff0c;一直比较最值 ST的创建 现在创建成功&#xff0c;是应该如何查询的问题 ST表的查询 虽然这两区间有重叠&#xff0c;但是可以一个往前数&#xff0c;一个往后数&#xff0c;互不影响 时间复杂度 创建st表的复杂度为n*logn 使用时的复杂度为O(…

【机器学习】K-近邻算法(KNN)介绍、应用及文本分类实现

一、引言 1.1 K-近邻算法&#xff08;KNN&#xff09;的基本概念 K-近邻算法&#xff08;K-Nearest Neighbors&#xff0c;简称KNN&#xff09;是一种基于实例的学习算法&#xff0c;它利用训练数据集中与待分类样本最相似的K个样本的类别来判断待分类样本所属的类别。KNN算法…

Golang 哈希表底层实现原理

1、本文讨论Golang的哈希表 Golang哈希表的实现&#xff0c;底层数据结构是数组单链表&#xff0c;链表节点由8个key、value和键的高八位组成的。为了方便理解&#xff0c;先简单看一个图快速理解。 我们来看一下Golang哈希表的结构体定义 简单介绍一下结构体中几个关键的…

.NET CORE 分布式事务(四) CAP实现最终一致性

目录 引言&#xff1a; 1.0 最终一致性介绍 2.0 CAP 2.0 架构预览 3.0 .NET CORE 结合CAP实现最终一致性分布式事务 3.1 准备工作(数据库&#xff0c;本文使用的是MySql) 3.1.1 数据模型 3.1.2 DbContext 3.1.3 数据库最终生成 3.2 Nuget引入 3.3 appsettings.json …