共享模型之无锁(三)
1.原子累加器
示例代码:
public class TestAtomicAdder {public static void main(String[] args) {for (int i = 0; i < 5; i++) {demo(() -> new AtomicLong(0),(adder) -> adder.getAndIncrement());}for (int i = 0; i < 5; i++) {demo(() -> new LongAdder(),(adder) -> adder.increment());}}/*** @param adderSupplier 提供者 无中生有 ()->结果* @param action 消费者 一个参数没结果* @param <T>*/private static <T> void demo(Supplier<T> adderSupplier, Consumer<T> action) {T adder = adderSupplier.get();long start = System.nanoTime();List<Thread> ts = new ArrayList<>();//4个线程,每个累加5万次,最终结果是200000for (int i = 0; i < 4; i++) {ts.add(new Thread(() -> {for (int j = 0; j < 50000; j++) {action.accept(adder);}}));}ts.forEach(t -> t.start());ts.forEach(t -> {try {t.join();} catch (InterruptedException e) {e.printStackTrace();}});long end = System.nanoTime();System.out.println(adder + " cost(ns):" + (end - start) / 1000_000);}
}
比较 AtomicLong 与 LongAdder:

LongAdder累加器性能提升的原因很简单,就是在有竞争时,设置多个累加单元,Therad-0累加Cell[0],而Thread-1累加Cell[1]… 最后将结果汇总.这样它们在累加时操作不同的Cell变量,因此减少了CAS重试失败,从而提高性能;
1.1.LongAdder源码分析
1>.LongAdder是并发大师Doug Lea(大哥李)的作品,设计的非常精巧;
2>.LongAdder类有几个关键域:
// 累加单元数组,懒惰初始化
// transient关键字作用在是序列化的时候保证这些域不会被序列化
transient volatile Cell[] cells;// 基础值,如果没有竞争,则用cas累加这个域
transient volatile long base;// 在cells创建或扩容时,置为1,表示加锁
transient volatile int cellsBusy;
3>.CAS锁
// 不要用于实践!!!
public class LockCas {private AtomicInteger state = new AtomicInteger(0);public void lock() {while (true) {if (state.compareAndSet(0, 1)) {break;}}}public void unlock() {log.debug("unlock...");state.set(0);}
}//测试代码
LockCas lock = new LockCas();
new Thread(() -> {log.debug("begin...");lock.lock();try {log.debug("lock...");sleep(1);} finally {lock.unlock();}
}).start();new Thread(() -> {log.debug("begin...");lock.lock();try {log.debug("lock...");} finally {lock.unlock();}
}).start();
4>.缓存行(hang)伪共享(一个缓存行加入了多个内存cell对象被称为伪共享)
其中Cell即为累加单元
// 防止缓存行伪共享,防止一个缓存行容纳多个内存cell对象
@sun.misc.Contended
static final class Cell {volatile long value;Cell(long x) { value = x; }// 最重要的方法, 用cas方式进行累加, prev表示旧值, next表示新值final boolean cas(long prev, long next) {return UNSAFE.compareAndSwapLong(this, valueOffset, prev, next);}// 省略不重要代码
}
1.1.1.LongAdder#add()源码
public void add(long x) {// as为累加单元数组// b为基础值// x为累加值Cell[] as; long b, v; int m; Cell a;// 进入 if 的两个条件// 1.as有值,表示已经发生过竞争, 进入if// 2.cas给base累加时失败了,表示base发生了竞争,进入ifif ((as = cells) != null || !casBase(b = base, b + x)) {// uncontended表示cell没有竞争boolean uncontended = true;// as还没有创建// 当前线程对应的cell还没有// cas给当前线程的cell累加失败uncontended=false(a为当前线程的cell)if (as == null || (m = as.length - 1) < 0 ||(a = as[getProbe() & m]) == null ||!(uncontended = a.cas(v = a.value, v + x)))// 进入cell数组创建、cell创建的流程longAccumulate(x, null, uncontended);}
}

1.1.2.Striped64#longAccumulate()源码
final void longAccumulate(long x, LongBinaryOperator fn,boolean wasUncontended) {int h;// 当前线程还没有对应的cell,需要随机生成一个h值用来将当前线程绑定到cellif ((h = getProbe()) == 0) {// 初始化probeThreadLocalRandom.current(); // force initialization// h对应新的probe值,用来对应(/占用)cell
h = getProbe();wasUncontended = true;}// collide为true表示需要扩容boolean collide = false; // True if last slot nonemptyfor (;;) {Cell[] as; Cell a; int n; long v;// 已经有了cellsif ((as = cells) != null && (n = as.length) > 0) {// 还没有cellif ((a = as[(n - 1) & h]) == null) {// 为cellsBusy加锁,创建cell,cell的初始累加值为x// 成功则 break,否则继续continue循环if (cellsBusy == 0) { // Try to attach new CellCell r = new Cell(x); // Optimistically createif (cellsBusy == 0 && casCellsBusy()) {boolean created = false;try { // Recheck under lockCell[] rs; int m, 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;}// 有竞争,改变线程对应的cell来重试caselse if (!wasUncontended) // CAS already known to failwasUncontended = true; // Continue after rehash// cas尝试累加,fn配合LongAccumulator不为null,配合LongAdder为null
else if (a.cas(v = a.value, ((fn == null) ? v + x :fn.applyAsLong(v, x))))break;// 如果cells长度已经超过了最大长度,或者已经扩容,改变线程对应的cell来重试caselse if (n >= NCPU || cells != as)collide = false; // At max size or stale// 确保collide为false进入此分支,就不会进入下面的else if进行扩容了
else if (!collide)collide = true;// 加锁else if (cellsBusy == 0 && casCellsBusy()) {try {if (cells == as) { // Expand table unless staleCell[] 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}// 改变线程对应的cell对象h = advanceProbe(h);}// 还没有cells,尝试给cellsBusy加锁else if (cellsBusy == 0 && cells == as && casCellsBusy()) {// 加锁成功,初始化cells,最开始长度为2,并填充一个cellboolean init = false;try { // Initialize tableif (cells == as) {Cell[] rs = new Cell[2];rs[h & 1] = new Cell(x);cells = rs;init = true;}} finally {cellsBusy = 0;}if (init)// 成功则break;break;}// 上两种情况失败,尝试给base累加else if (casBase(v = base, ((fn == null) ? v + x :fn.applyAsLong(v, x))))break; // Fall back on using base}
}


每个线程刚进入longAccumulate()时,会尝试对应(/占用)一个cell对象(找到一个位置)

1.1.3.LongAdder#sum()源码
public long sum() {Cell[] as = cells; Cell a;long sum = base;if (as != null) {for (int i = 0; i < as.length; ++i) {if ((a = as[i]) != null)//将Cells数组中的每个元素进行累加sum += a.value;}}return sum;
}
扩展: 缓存与内存的速度比较
1>.CPU内存结构:

2>.CPU访问不同的缓存和内存耗费的时钟周期

①.CPU与内存的速度差异很大,需要靠预读数据至缓存来提升效率;
②.缓存以缓存行为单位,每个缓存行对应着一块内存,一般是64byte(8个long);
③.缓存的加入会造成数据副本的产生,即同一份数据会缓存在不同核心的缓存行中;
④.CPU要保证数据的一致性,如果某个CPU核心更改了数据,其它CPU核心对应的整个缓存行(64byte)必须全部失效;
⑤.由于内存Cell是数组形式,在内存中是连续存储的,一个内存Cell为24字节(16字节的对象头和8字节的value),因此缓存行可以存下2个内存Cell对象.这样问题来了:
- Core-0要修改Cell[0]
- Core-1要修改Cell[1]
无论谁修改成功,都会导致对方Core的缓存行失效,比如Core-0中Cell[0]=6000,Cell[1]=8000要累加Cell[0]=6001,Cell[1]=8000,这时会让Core-1的缓存行失效;
⑥."@sun.misc.Contended"就是用来解决这个问题的,它的原理是在使用此注解的对象或字段的前后各增加128字节大小的padding(填充内存Cell),从而让CPU将(不同的)对象预读至缓存时占用不同的缓存行,这样不会造成对方缓存行的失效;
2.Unsafe对象
2.1.概述
1> Unsafe对象提供了非常底层的,操作内存、线程的方法,Unsafe对象不能直接调用,只能通过反射获得;
2>.示例代码:
public class TestUnsafeDemo1 {public static void main(String[] args) throws Exception {//通过反射获取到unsafe对象,这里的参数"theUnsafe"是固定的写法,对应着unsafe对象中的(类类型)成员变量Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");//由于unsafe对象中"theUnsafe"成员变量是"private"私有的,因此要设置访问权限theUnsafe.setAccessible(true);//通过成员变量获取到所属的unsafe对象//由于unsafe对象中"theUnsafe"成员变量是静态的,因此这里原本的对象参数就是nullUnsafe unsafe = (Unsafe) theUnsafe.get(null);System.out.println(unsafe); //sun.misc.Unsafe@7ea987ac}
}
3>.封装成工具类:
public class UnsafeAccessor {static Unsafe unsafe;static {try { Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");theUnsafe.setAccessible(true);unsafe = (Unsafe) theUnsafe.get(null);} catch (NoSuchFieldException | IllegalAccessException e) {throw new Error(e);}}static Unsafe getUnsafe() {return unsafe;}
}
2.2.Unsafe CAS操作
1>.使用Unsafe对象通过CAS机制线程安全的修改对象的成员变量:
@Slf4j
public class TestUnsafeCasDemo1 {public static void main(String[] args) throws Exception {//获取unsafe对象Unsafe unsafe = UnsafeAccessor.getUnsafe();//获取对象中某个成员变量的偏移地址long idOffset = unsafe.objectFieldOffset(Teacher.class.getDeclaredField("id"));long nameOffset = unsafe.objectFieldOffset(Teacher.class.getDeclaredField("name"));//执行CAS操作Teacher teacher = new Teacher();boolean b = unsafe.compareAndSwapInt(teacher, idOffset, 0, 1);boolean b1 = unsafe.compareAndSwapObject(teacher, nameOffset, null, "张三");//打印结果log.info(teacher.toString());}
}@Data
@NoArgsConstructor
class Teacher {volatile int id;volatile String name;
}//unsafe对象封装
class UnsafeAccessor {static Unsafe unsafe;static {try {Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");theUnsafe.setAccessible(true);unsafe = (Unsafe) theUnsafe.get(null);} catch (NoSuchFieldException | IllegalAccessException e) {throw new Error(e);}}static Unsafe getUnsafe() {return unsafe;}
}

2.3.模拟实现原子整数
1>.以转账为例:
public class TestCustomAtomicIntegerDemo1 {public static void main(String[] args) {Account1.demo(new MyAtomicInteger(200));}
}//自定义的原子整数类
class MyAtomicInteger implements Account1 {private volatile int value;private static final long valueOffset;private static final Unsafe UNSAFE;static {UNSAFE = UnsafeAccessorUtil.getUnsafe();try {valueOffset = UNSAFE.objectFieldOffset(MyAtomicInteger.class.getDeclaredField("value"));} catch (NoSuchFieldException e) {e.printStackTrace();throw new RuntimeException();}}public MyAtomicInteger(int value) {this.value = value;}public int getValue() {return this.value;}public void decrement(int amount) {while (true) {int prev = this.value;int next = prev - amount;if (UNSAFE.compareAndSwapInt(this, valueOffset, prev, next)) {break;}}}@Overridepublic Integer getBalance() {return this.getValue();}@Overridepublic void withDraw(int amount) {this.decrement(amount);}
}//unsafe工具类
class UnsafeAccessorUtil {static Unsafe unsafe;static {try {Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");theUnsafe.setAccessible(true);unsafe = (Unsafe) theUnsafe.get(null);} catch (NoSuchFieldException | IllegalAccessException e) {throw new Error(e);}}static Unsafe getUnsafe() {return unsafe;}
}//封装转账相关的方法
interface Account1 {//获取余额Integer getBalance();//取款void withDraw(int amount);/*** 方法内会启动20个线程,每个线程做(-10元)的操作* 如果初始余额为200那么正确的结果应当是 0*/static void demo(Account1 account) {List<Thread> ts = new ArrayList<>();long start = System.nanoTime();for (int i = 0; i < 20; i++) {ts.add(new Thread(() -> {account.withDraw(10);}));}ts.forEach(Thread::start);ts.forEach(t -> {try {t.join();} catch (InterruptedException e) {e.printStackTrace();}});long end = System.nanoTime();System.out.println(account.getBalance()+ " cost: " + (end - start) / 1000_000 + " ms");}
}

相关文章:
共享模型之无锁(三)
1.原子累加器 示例代码: public class TestAtomicAdder {public static void main(String[] args) {for (int i 0; i < 5; i) {demo(() -> new AtomicLong(0),(adder) -> adder.getAndIncrement());}for (int i 0; i < 5; i) {demo(() -> new LongAdder(),(…...
微信小程序 Springboot校运会高校运动会管理系统
3.1小程序端 小程序登录页面,用户也可以在此页面进行注册并且登录等。 登录成功后可以在我的个人中心查看自己的个人信息或者修改信息等 在广播信息中我们可以查看校运会发布的一些信息情况。 在首页我们可以看到校运会具体有什么项目运动。 在查看具体有什么活动我…...
走进独自开,带你轻松干副业
今天给大家分享一个开发者的福利平台——独自开(点击直接注册),让你在家就能解决收入问题。 文章目录一、平台介绍二、系统案例三、获取收益四、使用平台1、用户注册2、用户认证3、任务报价五、文末总结一、平台介绍 简单说明 独自开信息科技…...
SpringBoot+Vue实现师生健康信息管理系统
文末获取源码 开发语言:Java 框架:springboot JDK版本:JDK1.8 服务器:tomcat7 数据库:mysql 5.7/8.0 数据库工具:Navicat11 开发软件:eclipse/myeclipse/idea Maven包:Maven3.3.9 浏…...
数据库第四章节第三次作业内容
1、显示所有职工的基本信息。 2、查询所有职工所属部门的部门号,不显示重复的部门号。 3、求出所有职工的人数。 4、列出最高工和最低工资。 5、列出职工的平均工资和总工资。 6、创建一个只有职工号、姓名和参加工作的新表,名为工作日期表…...
一篇五分生信临床模型预测文章代码复现——FIgure 9.列线图构建,ROC分析,DCA分析 (四)
之前讲过临床模型预测的专栏,但那只是基础版本,下面我们以自噬相关基因为例子,模仿一篇五分文章,将图和代码复现出来,学会本专栏课程,可以具备发一篇五分左右文章的水平: 本专栏目录如下: Figure 1:差异表达基因及预后基因筛选(图片仅供参考) Figure 2. 生存分析,…...
神经网络实战--使用迁移学习完成猫狗分类
前言: Hello大家好,我是Dream。 今天来学习一下如何使用基于tensorflow和keras的迁移学习完成猫狗分类,欢迎大家一起前来探讨学习~ 本文目录:一、加载数据集1.调用库函数2.加载数据集3.数据集管理二、猫狗数据集介绍1.猫狗数据集介…...
Attention机制 学习笔记
学习自https://easyai.tech/ai-definition/attention/ Attention本质 Attention(注意力)机制如果浅层的理解,跟他的名字非常匹配。他的核心逻辑就是“从关注全部到关注重点”。 比如我们人在看图片时,对图片的不同地方的注意力…...
数据类型与运算符
1.字符型作用: 字符型变量用于显示单个字符语法: char cc a ;注意1: 在显示字符型变量时,用单引号将字符括起来,不要用双引号注意2: 单引号内只能有一个字符,不可以是字符串C和C中字符型变量只占用1个字节。字符型变是并不是把字符本身放到内存中存储&am…...
算法刷题-二叉树的锯齿形层序遍历、用栈实现队列 栈设计、买卖股票的最佳时机 IV
文章目录二叉树的锯齿形层序遍历(树、广度优先搜索)用栈实现队列(栈、设计)买卖股票的最佳时机 IV(数组、动态规划)二叉树的锯齿形层序遍历(树、广度优先搜索) 给定一个二叉树&…...
华为OD机试 - 最小传递延迟(Python)| 代码编写思路+核心知识点
最小传递延迟 题目 通讯网络中有 N 个网络节点 用 1 ~ N 进行标识 网络通过一个有向无环图进行表示 其中图的边的值,表示节点之间的消息传递延迟 现给定相连节点之间的延时列表 times[i]={u,v,w} 其中 u 表示源节点,v 表示目的节点,w 表示 u 和 v 之间的消息传递延时 请计…...
集中供热调度系统天然气仪表内网仪表图像识别案例
一、项目需求 出于能耗采集与冬季集中供暖工作的节能和能耗分析需要,要采集现场的6块天然气表计,并存储进入客户的mySQL数据库中,现场采集的表计不允许接线,且网络环境为内网环境,需要采集表计数据并存入数据库&#…...
笔试题-2023-复旦微-数字IC设计【纯净题目版】
回到首页:2023 数字IC设计秋招复盘——数十家公司笔试题、面试实录 推荐内容:数字IC设计学习比较实用的资料推荐 题目背景 笔试时间:2022.07.26应聘岗位:数字前端工程师笔试时长:120min笔试平台:赛码题目类型:基础题(10道)、选做题(10道)、验证题(5道)主观评价 难…...
【Linux】冯诺依曼体系结构和操作系统概念
文章目录🎪 冯诺依曼体系结构🚀1.体系概述🚀2.CPU和内存的数据交换🚀3.体系结构中数据的流动🎪 操作系统概念理解🚀1.简述🚀2.设计目的🚀3.定位🚀4.理解🚀5.管…...
HTML5之HTML基础学习笔记
列表标签 列表的应用场景 场景:在网页中按照行展示关联性的内容,如:新闻列表、排行榜、账单等特点:按照行的方式,整齐显示内容种类:无序列表、有序列表、自定义列表 这是老师PPT上的内容, 列表…...
FreeRTOS信号量 | FreeRTOS十
目录 说明: 一、信号量 1.1、信号量简介 1.2、信号量特点 二、二值信号量 2.1、二值信号量简介 2.2、获取与释放二值信号量函数 2.3、二值信号量使用过程与相关API函数 2.4、创建二值信号量函数了解 2.5、释放二值信号量了解 2.6、获取二值信号量了解 三…...
【SpringBoot】SpringBoot常用注解
一、前言首先这里说的SpringBoot常用注解是指在我们开发项目过程中,我们经常使用的注解,包含Spring、SpringBoot、SpringCloud、SpringMVC等这些框架中的注解,而不仅仅是SpringBoot中的注解。这里只是作一个注解列举,每个注解具体…...
数据一致性
目录一、AOP 动态代理切入方法(1) Aspect Oriented Programming(2) 切入点表达式二、SpringBoot 项目扫描类(1) ResourceLoader 扫描类(2) Map 的 computeIfAbsent 方法(3) 反射几个常用 api① 创建一个测试注解② 创建测试 PO 类③ 反射 api 获取指定类的指定注解信息(4) 返回…...
Docker不做虚拟化内核,对.NET有什么影响?
引子前两天刷抖音,看见了这样一个问题。问题:容器化不做虚拟内核,会有什么弊端?Java很多方法会跟CPU的核数有关,这个时候调用系统函数,读到的是宿主机信息,而不是我们限制资源的大小。思考&…...
HTML总结
CSS代码风格 空格规范: 1. 属性值前面,冒号后面,保留一个空格; 2. 选择器(标签)和大括号中间保留空格。 基本语法概述: 1.HTML标签是由尖括号包围的关键词,如<html> 2.HTM…...
centos 7 部署awstats 网站访问检测
一、基础环境准备(两种安装方式都要做) bash # 安装必要依赖 yum install -y httpd perl mod_perl perl-Time-HiRes perl-DateTime systemctl enable httpd # 设置 Apache 开机自启 systemctl start httpd # 启动 Apache二、安装 AWStats࿰…...
《从零掌握MIPI CSI-2: 协议精解与FPGA摄像头开发实战》-- CSI-2 协议详细解析 (一)
CSI-2 协议详细解析 (一) 1. CSI-2层定义(CSI-2 Layer Definitions) 分层结构 :CSI-2协议分为6层: 物理层(PHY Layer) : 定义电气特性、时钟机制和传输介质(导线&#…...
从零实现STL哈希容器:unordered_map/unordered_set封装详解
本篇文章是对C学习的STL哈希容器自主实现部分的学习分享 希望也能为你带来些帮助~ 那咱们废话不多说,直接开始吧! 一、源码结构分析 1. SGISTL30实现剖析 // hash_set核心结构 template <class Value, class HashFcn, ...> class hash_set {ty…...
鸿蒙中用HarmonyOS SDK应用服务 HarmonyOS5开发一个生活电费的缴纳和查询小程序
一、项目初始化与配置 1. 创建项目 ohpm init harmony/utility-payment-app 2. 配置权限 // module.json5 {"requestPermissions": [{"name": "ohos.permission.INTERNET"},{"name": "ohos.permission.GET_NETWORK_INFO"…...
HTML前端开发:JavaScript 常用事件详解
作为前端开发的核心,JavaScript 事件是用户与网页交互的基础。以下是常见事件的详细说明和用法示例: 1. onclick - 点击事件 当元素被单击时触发(左键点击) button.onclick function() {alert("按钮被点击了!&…...
【JavaWeb】Docker项目部署
引言 之前学习了Linux操作系统的常见命令,在Linux上安装软件,以及如何在Linux上部署一个单体项目,大多数同学都会有相同的感受,那就是麻烦。 核心体现在三点: 命令太多了,记不住 软件安装包名字复杂&…...
以光量子为例,详解量子获取方式
光量子技术获取量子比特可在室温下进行。该方式有望通过与名为硅光子学(silicon photonics)的光波导(optical waveguide)芯片制造技术和光纤等光通信技术相结合来实现量子计算机。量子力学中,光既是波又是粒子。光子本…...
2025季度云服务器排行榜
在全球云服务器市场,各厂商的排名和地位并非一成不变,而是由其独特的优势、战略布局和市场适应性共同决定的。以下是根据2025年市场趋势,对主要云服务器厂商在排行榜中占据重要位置的原因和优势进行深度分析: 一、全球“三巨头”…...
Leetcode33( 搜索旋转排序数组)
题目表述 整数数组 nums 按升序排列,数组中的值 互不相同 。 在传递给函数之前,nums 在预先未知的某个下标 k(0 < k < nums.length)上进行了 旋转,使数组变为 [nums[k], nums[k1], …, nums[n-1], nums[0], nu…...
Python 高级应用10:在python 大型项目中 FastAPI 和 Django 的相互配合
无论是python,或者java 的大型项目中,都会涉及到 自身平台微服务之间的相互调用,以及和第三发平台的 接口对接,那在python 中是怎么实现的呢? 在 Python Web 开发中,FastAPI 和 Django 是两个重要但定位不…...


