【JUC源码专题】Striped64 核心源码分析(JDK8)

news2024/12/25 9:08:14

文章目录

    • 核心变量
      • 缓存行填充
    • longAccumulate 方法
      • 方法概览
      • cells 数组已初始化
        • 重新计算随机数
        • 扩容前置条件
      • cells 数组未初始化
      • cas 更新 Base

Striped64 的核心是通过分治思想将对 base 的竞争分散到不同的 cell 单元中。

核心变量

    // 通过分治的思想将对 base 的竞争分散到不同的 cell 单元中。
    transient volatile Cell[] cells;

    // 无竞争时直接更新 base
    // 有竞争时同时更新 base 和 cells 数组
    transient volatile long base;

    // Spinlock 自旋锁
    transient volatile int cellsBusy;

    // Cell 对象封装了每个计算单元的值和常用方法。
    @sun.misc.Contended static final class Cell {
        volatile long value;
        Cell(long x) { value = x; }
        final boolean cas(long cmp, long val) {
            return UNSAFE.compareAndSwapLong(this, valueOffset, cmp, val);
        }

        // Unsafe mechanics
        private static final sun.misc.Unsafe UNSAFE;
        private static final long valueOffset;
        static {
            try {
                UNSAFE = sun.misc.Unsafe.getUnsafe();
                Class<?> ak = Cell.class;
                // 获取 value 属性偏移量
                valueOffset = UNSAFE.objectFieldOffset
                    (ak.getDeclaredField("value"));
            } catch (Exception e) {
                throw new Error(e);
            }
        }
    }

缓存行填充

使用 @Contended 注解来支持缓存行填充,避免 value 的缓存行伪共享。
Contended

longAccumulate 方法

一言以蔽之:合理分散线程竞争。将对 base 的 cas 分散到 base 和 cells 数组中。

方法概览

    final void longAccumulate(long x, LongBinaryOperator fn,
                              boolean wasUncontended) {
        int h;
        // 初始化线程随机数
        if ((h = getProbe()) == 0) {
            ThreadLocalRandom.current(); // force initialization
            h = getProbe();
            // 为 true 表示没有竞争 
            wasUncontended = true;
        }
        boolean collide = false;                // True if last slot nonempty
        done: for (;;) {
            Cell[] as; Cell a; int n; long v;
            // cells 数组已初始化
            if ((as = cells) != null && (n = as.length) > 0){...}
            // cells 数组未初始化
            else if (cellsBusy == 0 && cells == as && casCellsBusy()){...}
            // 未竞争到自旋锁的线程尝试直接更新 base
            else if (casBase(v = base,  
                 (fn == null) ? v + x : fn.applyAsLong(v, x)))
                 break done;
        }
    }
  • if ((as = cells) != null && (n = as.length) > 0){...}
    • 若 cell 对象未初始化,则初始化 cell 对象
    • 若 cell 对象已初始化则尝试更新 base,更新成功直接 break
    • cell 数组长度小于 CPU 个数且 Cell 数组的引用未发生改变则扩容,扩容完成后 continue
  • else if (cellsBusy == 0 && cells == as && casCellsBusy()){...}
    • CAS 修改 cellsBusy 为 1 成功的线程才能进行 cells 数组初始化
  • else if (casBase(v = base, (fn == null) ? v + x : fn.applyAsLong(v, x)))
    • 没有竞争到初始化 cells 数组自旋锁的线程尝试 cas 修改 base,压榨 CPU,避免空转

cells 数组已初始化

            if ((as = cells) != null && (n = as.length) > 0) {
                if ((a = as[(n - 1) & h]) == null) { 
                    // 初始化 cell 对象
                    // 没有其他线程获取到自旋锁,先获取锁再创建 Cell 对象
                    if (cellsBusy == 0) {       // Try to attach new Cell
                        // 先创建 Cell 对象,后上锁,减少获取锁以后耗时
                        Cell r = new Cell(x);   // Optimistically(乐观) create
                        // 再次判断有没有线程获取自旋锁,若没有则 CAS 更新 cellsBusy
                        if (cellsBusy == 0 && casCellsBusy()) {
                            // 此时已成功将 cellsBusy 修改为 1,即获取到自旋锁
                            boolean created = false;
                            try {               // Recheck under lock
                                Cell[] rs; int m, j;
                                // 再次检测 cells 数组有效且没有线程初始化当前单元 j。
                                // 理由是当前线程 CAS 获取锁之前,可能有其他线程获取到了锁,然后初始化了单元 j
                                if ((rs = cells) != null &&
                                    (m = rs.length) > 0 &&
                                    rs[j = (m - 1) & h] == null) {
                                    rs[j] = r;
                                    created = true;
                                }
                            } finally {
                                // 释放锁
                                cellsBusy = 0;
                            }
                            // 创建成功,退出循环
                            if (created)
                                break;
                            continue;           // Slot is now non-empty
                        }
                    }
                    collide = false;
                }
                else if (!wasUncontended)       // CAS already known to fail
                    wasUncontended = true;      // Continue after rehash
                // CAS 尝试更新 Cell 对象的值,若更新成功直接返回
                else if (a.cas(v = a.value, ((fn == null) ? v + x :
                                             fn.applyAsLong(v, x))))
                    break;
                // Cell 数组长度大于等于 CPU 个数或者 Cell 数组的引用发生了改变
                else if (n >= NCPU || cells != as)
                    // 这里把 collide 改为 FALSE 是为了下个线程走到 else if (!collide) 这个条件中
                    // 从而避免进入 else if (cellsBusy == 0 && casCellsBusy()) 扩容操作
                    collide = false;            // At max size or stale
                else if (!collide)
                    collide = true;
                // 竞争自旋锁
                else if (cellsBusy == 0 && casCellsBusy()) {
                    try {
                        // cells == as 说明数组引用没有变化,则扩容数组为原先的 2 倍
                        if (cells == as) {      // Expand table unless stale
                            Cell[] rs = new Cell[n << 1];
                            // 拷贝数组元素
                            for (int i = 0; i < n; ++i)
                                rs[i] = as[i];
                            cells = rs;
                        }
                    } finally {
                        cellsBusy = 0;
                    }
                    collide = false;
                    continue;                   // Retry with expanded table
                }
                // 执行到这一行代码
                h = advanceProbe(h);
            }

重新计算随机数

else if (!wasUncontended)

  • wasUncontendedfalse,说明存在竞争,会进入 else if (!wasUncontended) 这个条件判断
  • 然后执行 wasUncontended = true; 这行代码,将 wasUncontended 更新为 true
  • 然后往下执行到 h = advanceProbe(h); 进行 rehash。
  • rehash 之后再次判断 (a = as[(n - 1) & h]) == null
    else if (!wasUncontended)   // CAS already known to fail
        wasUncontended = true;

Pasted image 20221102111441

扩容前置条件

else if (n >= NCPU || cells != as)

  • 这里把 collide 改为 FALSE 是为了下个线程走到 else if (!collide) 这个条件中
  • 从而避免进入 else if (cellsBusy == 0 && casCellsBusy()) 扩容操作
  • 因为 JAVA 中没有 goto 关键字
                else if (n >= NCPU || cells != as)
                    collide = false;            // At max size or stale
                else if (!collide)
                    collide = true;
                else if (cellsBusy == 0 && casCellsBusy()) {...}

在这里插入图片描述

cells 数组未初始化

  • 此时 cells 数组未初始化,CAS 修改标志位 CELLSBUSY 成功的线程开始初始化全局数组 cells
  • 获取到 cellsbusy 的线程,执行这些代码时,为线程安全
  • 通过一个变量 cellsbusy,达到一个 CAS 的 CPU 原语,构建多条指令的原子性
            else if (cellsBusy == 0 && cells == as && casCellsBusy()) {
                boolean init = false;
                try {
                    // Initialize table
                    // 若 cells != as 说明存在其他线程修改了 cells 数组的引用
                    // cells == as 说明数组引用没有变化,则初始化 cells 数组
                    if (cells == as) {
                        Cell[] rs = new Cell[2];
                        // 初始化数组时,顺便初始化一个 cell 对象
                        rs[h & 1] = new Cell(x);
                        cells = rs;
                        init = true;
                    }
                } finally {
                    cellsBusy = 0; // 释放锁
                }
                // 初始化成功说明 x 已经存放到 cell 对象中放入 cells 数组,直接退出即可
                if (init)
                    break;
            }

cas 更新 Base

  • 没有竞争到初始化 cells 数组自旋锁的线程尝试 cas 修改 base,压榨 CPU,避免空转
  • 修改成功直接 break 退出循环
else if (casBase(v = base, ((fn == null) ? v + x : fn.applyAsLong(v, x))))
                break;                          // Fall back on using base

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

多线程加强

1, 线程状态 1.1 概述 一个线程从创建,运行,到最后销毁的这个过程称之为线程的生命周期,在这个生命周期过程中线程可能会经历如下几个状态:新建状态,就绪状态,运行状态,阻塞状态,死亡状态。 1.2 测试 public class ThreadTest {static String des;//main线程/主线…

【javaEE】网络原理(传输层Part3)

努力经营当下&#xff0c;直至未来明朗&#xff01; 文章目录前言TCP相关机制7. 延迟应答8. 捎带应答TCP补充【面向字节流】【TCP中的异常处理】另&#xff08;含面试题&#xff09;TCP小结THINK前言 一个人最大的痛苦来源于对自己无能的愤怒 Hi&#xff0c;这里还是不想秃头…

数据结构与算法基础-学习-01-线性表之顺序表-初始化、销毁、清理、获取长度、判断为空、获取元素等实现

一、测试环境 名称值cpu12th Gen Intel Core™ i7-12700H操作系统CentOS Linux release 7.9.2009 (Core)内存3G逻辑核数2gcc 版本4.8.5 20150623 二、个人理解 数据结构分为逻辑结构和物理结构&#xff08;也称为存储结构&#xff09;。 1、逻辑结构 逻辑结构又可以分为以下…

python学习笔记(09)---(正则表达式)

第七章 内置容器&#xff08;五&#xff09; 8.正则表达式 &#xff08;1&#xff09;正则表达式用于描述字符串的复杂文本规则的代码&#xff0c;一般用于查询匹配 &#xff08;2&#xff09;常见元字符 <1> 作用&#xff1a;表示特殊含义&#xff0c;一般为范围性…

MySQL数据库期末考试试题及参考答案(09)

版权声明 本文原创作者&#xff1a;谷哥的小弟作者博客地址&#xff1a;http://blog.csdn.net/lfdfhl 一、 填空题 MySQL提供的____命令可以将数据库导出成SQL脚本&#xff0c;以实现数据的备份。mysqldump命令备份多个数据库时&#xff0c;数据库名称之间用____隔开。MySQL中…

Java-1123

Jedis 介绍&#xff1a;Jedis是一款java操作redis数据库的工具&#xff08;类似JDBC&#xff09; 使用步骤&#xff1a; 下载jedis的jar包使用 快速入门 // 1.获取连接 Jedis jedis new Jedis("localhost",6379); // 2.操作 jedis.set("name","z…

c++ 深度拷贝和浅拷贝

一、简介 由于20年转行之后一直用的是halcon 和c# &#xff0c;C就此搁浅&#xff0c;最近开始搞pcl慢慢的又重拾起来&#xff0c;对于深拷贝和浅拷贝我知道是什么原因造成的&#xff0c;也知道如何解决&#xff0c;但是突然被人问得时候又有点蒙蔽&#xff0c;因此做一个简单…

MCE 天然化合物相关库更新

随着现代化合物分离和鉴定技术的不断提高&#xff0c;以及人们对自然界生物多样性的持续研究&#xff0c;越来越多的天然产物被用于新药研究开发。据统计&#xff0c;截至 2013 年&#xff0c;有 547 个天然产物及天然产物衍生物被 FDA 批准上市&#xff0c;占所有 FDA 批准上市…

高通骁龙处理器天梯排行榜2022 骁龙处理器发布时间排行

骁龙处理器天梯图2022最新10月更新榜单 1、高通骁龙8 Gen1 2、高通骁龙8 Gen1 3、高通骁龙888 Plus 4、高通骁龙888 我用的手机就是活动时7.5折抢购的 点击开抢 http://shouji.adiannao.cn/7 1、工艺&#xff1a;搭载最新一代5nm制作工艺&#xff0c;为用户带来最强的处理器性能…

[附源码]Python计算机毕业设计大学生兼职系统

项目运行 环境配置&#xff1a; Pychram社区版 python3.7.7 Mysql5.7 HBuilderXlist pipNavicat11Djangonodejs。 项目技术&#xff1a; django python Vue 等等组成&#xff0c;B/S模式 pychram管理等等。 环境需要 1.运行环境&#xff1a;最好是python3.7.7&#xff0c;我…

STM32中的独立看门狗和窗口看门狗

独立看门狗和窗口看门狗有什么区别&#xff1f;一、前言二、独立看门狗三、窗口看门狗四、结语一、前言 在早期的MCU中是没有看门狗这种东西的&#xff0c;所以产品就很容易出现死机&#xff0c;跑飞的情况。为了避免这种情况的出现&#xff0c;后期的MCU都集成了看门狗的功能…

CI/CD Jenkins容器化安装部署

环境准备 docker安装 jenkins镜像安装 CI/CD CI:持续集成 CD:连续交付 环境准备 下载前推荐先看下Jenkins硬件和软件要求部分 Jenkins官方文档 Jenkins下载 虚拟机 docker 对配置的要求 CentOS安装可跳转 VMware安装CentOS CentOS 7.8 // 查看CentOS版本命令 cat /etc…

牛客网语法篇练习循环控制(一)

1.2023年就要来到了&#xff0c;KiKi要对他的n位好朋友说n遍”Happy new year!Good luck!”的祝福语。 n int(input()) for i in range(n):print(Happy new year!Good luck!) 2.任意输入一个正整数N&#xff0c;统计1~N之间奇数的个数和偶数的个数&#xff0c;并输出。 num …

【网页设计】基于HTML在线商城购物项目设计与实现-----电脑商城6页带视频 带报告3000字

⛵ 源码获取 文末联系 ✈ Web前端开发技术 描述 网页设计题材&#xff0c;DIVCSS 布局制作,HTMLCSS网页设计期末课程大作业 | 在线商城购物 | 水果商城 | 商城系统建设 | 多平台移动商城 | H5微商城购物商城项目 | HTML期末大学生网页设计作业 HTML&#xff1a;结构 CSS&#…

java开发者工具IDEA自定义设置主题/字体/字号大小

IDEA自定义设置主题 第一步&#xff1a;点击工具栏上的“File”选项。 第二步&#xff1a;选择“Settings…”选项。 第三步&#xff1a;点击“Appearance & Behavior > Appearance”选项。 第四步&#xff1a;点击右侧"Theme"下拉框&#xff0c;选择自己喜…

动态爬虫管理平台构建与实现_kaic

目 录 第1章 绪论 1.1背景与意义 1.2主题网络爬虫的国内外研究现状 1.2.1主题辨别算法及平台构建 1.2.2主题爬虫系统 1.3本文的研究内容 第二章 主题网络爬虫的体系结构 2.1组成模块 2.1.1基本组成 2.1.2基本流程 2.2主题页面的分布特性 2.2.1 Hub/Authority特性 2.2.2 Linkag…

一篇文章带你搞懂前端Cookie

文章目录一. 前言(一些废话)二. Cookie1. 为什么会有Cookie2. Cookie的特性3. 浏览器Cookie4. 跨站和跨域三. Cookie操作1. 获取Cookie2. 设置Cookie3. 修改Cookie4. 删除Cookie四. Cookie属性1. Cookie属性Domain2. Cookie属性path3. Cookie属性expires4. Cookie属性max-age5.…

大数据_数据中台_数据分层

目录 分层总览 ODS:操作数据层 DIM:维度数据层 DWD:明细数据层 DWS:汇总数据层 ADS:数据应用层 CDM:公共数据层 数据层级调用原则 分层总览 ODS:操作数据层 ODS(Operate Data Store&#xff09;,ODS层数据是数据仓库的第一层数据&#xff0c;是业务数据库的原始数据的复…

Oracle Primavera Unifier uDesigner 资产管理器(Asset Manager )

目录 前言 特点 优势 维护管理 独立或集成 事务管理 资产组合管理 投资组合管理能力 可持续性和能源管理 单一集成设施和资产生命周期管理 移动应用 前言 Oracle Primavera Unifier 为组织提供设施管理的行业最佳实践解决方案。从设计、构建、运行和维护&#xff0…

【深入理解Kotlin协程】Google的工程师们是这样理解Flow的?

Question&#xff1a;why there is a Flow in kotlin? 问这个问题就好比在问为什么那里会有一座山存在&#xff0c;嗯&#xff0c;这貌似是一个哲学问题。当然&#xff0c;对于kotlin中的Flow的理解可能不会上升到这么高的哲学层次&#xff0c;对于Flow相关的Api掌握并使用它…