Java 并发编程之CAS 和 Unsafe类本地使用方法

news/2024/4/19 11:13:29/文章来源:https://blog.csdn.net/Urbanears/article/details/128097550

Java 并发编程之CAS 和 Unsafe类本地使用方法

文章目录

  • Java 并发编程之CAS 和 Unsafe类本地使用方法
    • CAS原理与Unsafe类
    • Unsafe类本地使用方法

CAS原理与Unsafe类

我们知道保证线程安全的三个要素是原子性,可见性,有序性

CAS(Compare And Swap),指令级别保证某一内存地址V上的值的更新修改是一个原子操作
需要三个值:

  • 内存地址V

  • 该线程拿到的值A

  • 期望更新后的值B

思路:如果地址V上的实际值和该线程拿到的值A相等,就给地址V赋给新值B,如果不是,不做任何操作。
循环(死循环,自旋)里不断的进行CAS操作

JDK里为我们提供了这些原子操作类

  • 更新基本类型类:AtomicBooleanAtomicIntegerAtomicLongAtomicReference
  • 更新数组类:AtomicIntegerArrayAtomicLongArrayAtomicReferenceArray
  • 更新引用类型:AtomicReferenceAtomicMarkableReferenceAtomicStampedReference
  • 原子更新字段类: AtomicReferenceFieldUpdaterAtomicIntegerFieldUpdaterAtomicLongFieldUpdater

观察这些类的源码我们可以发现,CAS底层的原理实现都需要借助一个Unsafe类来实现,比如对于AtomicInteger类的compareAndSet方法:

    public final boolean compareAndSet(int expect, int update) {return unsafe.compareAndSwapInt(this, valueOffset, expect, update);}

代码中unsafe变量的初始化:

private static final Unsafe unsafe = Unsafe.getUnsafe();

于是,为了尝试使用CAS在本地操作,模仿了上面的代码和初始化,尝试在本地进行测试,代码如下:

public class CASTest {static volatile long valueOffset;// Unsafe类初始化static Unsafe unsafe = sun.misc.Unsafe.getUnsafe();static {
//        initUnsafe();try {valueOffset = unsafe.objectFieldOffset(CASTest.class.getDeclaredField("value"));} catch (Exception ex) { throw new Error(ex); }}private static void initUnsafe() {try {Field f = Unsafe.class.getDeclaredField("theUnsafe");f.setAccessible(true);Unsafe unsafe1 =  (Unsafe) f.get(null);unsafe = unsafe1;} catch (IllegalAccessException e) {e.printStackTrace();} catch (NoSuchFieldException e) {e.printStackTrace();}}volatile int value;public CASTest(int value) {this.value = value;}// 测试cas操作public void cas() {System.out.println(unsafe.compareAndSwapInt(this, valueOffset , 5, 10));System.out.println("this.value:" + this.value);System.out.println(unsafe.compareAndSwapInt(this, valueOffset , 5, 20));System.out.println("this.value:" + this.value);}public static void main(String[] args) {new CASTest(5).cas();}
}

但是执行后发现,会报错:

Exception in thread "main" java.lang.ExceptionInInitializerError
Caused by: java.lang.SecurityException: Unsafeat sun.misc.Unsafe.getUnsafe(Unsafe.java:90)at com.lagou.concurrent.demo.test.CASTest.<clinit>(CASTest.java:9)

查询相关资料后发现,在使用该getUnsafe方法是,会判断classLoader的类型,如果不是systemClassLoader则会抛出SecurityException(“Unsafe”)异常,所以用户编写的程序使用不了unsafe实例。

那如果我们想本地实现可以怎么办呢?

Unsafe类本地使用方法

下面给出一个本地使用Unsafe类初始化的方法,也是网上使用比较多的方法

private static void initUnsafe() {try {Field f = Unsafe.class.getDeclaredField("theUnsafe");f.setAccessible(true);Unsafe unsafe1 =  (Unsafe) f.get(null);unsafe = unsafe1;} catch (IllegalAccessException e) {e.printStackTrace();} catch (NoSuchFieldException e) {e.printStackTrace();}}

使用该初始化方法更新上面的测试代码:

public class CASTest {static volatile long valueOffset;static Unsafe unsafe;static {initUnsafe();try {valueOffset = unsafe.objectFieldOffset(CASTest.class.getDeclaredField("value"));} catch (Exception ex) { throw new Error(ex); }}private static void initUnsafe() {try {Field f = Unsafe.class.getDeclaredField("theUnsafe");f.setAccessible(true);Unsafe unsafe1 =  (Unsafe) f.get(null);unsafe = unsafe1;} catch (IllegalAccessException e) {e.printStackTrace();} catch (NoSuchFieldException e) {e.printStackTrace();}}volatile int value;public CASTest(int value) {this.value = value;}public void cas() {System.out.println(unsafe.compareAndSwapInt(this, valueOffset , 5, 10));System.out.println("this.value:" + this.value);System.out.println(unsafe.compareAndSwapInt(this, valueOffset , 5, 20));System.out.println("this.value:" + this.value);}public static void main(String[] args) {new CASTest(5).cas();}
}

正常执行,且输出结果为:

true
this.value:10
false
this.value:10

对于输出结果,根据CAS原理我们分析可知,初始时,我们赋值value=5,那么第一个CAS操作时valueOffset地址对应的value值=5,与compareAndSwapInt参数里的期望值5匹配,因此CAS操作成功返回true,同时value值被赋为10。同理,第二次CAS操作取valueOffset地址对应的value=10,与方法中的期望值5不匹配,则CAS操作失败返回false,此时value的值仍为10。

如果我们把第二次CAS操作的期望值设成10,那么最终的返回value值会为20。

true
this.value:10
true
this.value:20

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

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

相关文章

Kubernetes之Pod初始化容器

Kubernetes之Pod初始化容器 概述 ​ 初始化是很多编程语言普遍关注的问题&#xff0c;甚至有些编程语言直接支持模式构造来生成初始化程序&#xff0c;这些用于进行初始化的程序结构称为初始化器或初始化列表。初始化代码要首先运行&#xff0c;且只能运行一次&#xff0c;它们…

CAPM资产定价模型

本文是Quantitative Methods and Analysis: Pairs Trading此书的读书笔记。 CAPM(Capital Asset Pricing Model)资产定价模型 这个模型在金融界的影响就是beta这个词的使用。CAPM模型用两个部分的回报之和来解释一个资产的回报。其中一个部分是指市场&#xff08;称为market)…

时间序列:时间序列模型---自回归过程(AutoRegressive Process)

本文是Quantitative Methods and Analysis: Pairs Trading此书的读书笔记。 这次我们构造一个由无限的白噪声实现&#xff08;white noise realization) 组成的时间序列&#xff0c;即。这个由无限数目的项组成的值却是一个有限的值&#xff0c;比如时刻的值为&#xff0c; 而…

Magic Leap 2设计和开发幕后花絮

Magic Leap今年发布新款AR头显Magic Leap 2&#xff0c;相比于上一代Magic Leap 1&#xff0c;新品更专注于B端场景&#xff0c;自公布以来&#xff0c;Magic Leap不仅对公司策略、理念更加透明&#xff0c;也不断公开ML2产品设计背后的思考。相比于ML1&#xff0c;ML2的设计有…

粒子群算法查找函数最小值

实现 函数 F01、F06 的优化测试 以下内容是现有算法的运行结果、调参分析、及代码实现&#xff0c;用于给其他人提供参考&#xff0c;懒得改了hh 1. 运行结果 参数 w 0.5 &#xff08;可更改&#xff09; c1 2.0 &#xff08;可更改&#xff09; c2 2.0 &#xff08;可更改&…

Echart 柱状图,X轴斜着展示

option { color: [‘#3398DB’], tooltip: { trigger: ‘axis’, axisPointer: { // 坐标轴指示器&#xff0c;坐标轴触发有效 type: ‘shadow’ // 默认为直线&#xff0c;可选为&#xff1a;‘line’ | ‘shadow’ } }, grid: { left: ‘3%’, right: ‘4%’, bottom: ‘3%’…

iPhone升级iOS 16后出现提示“面容ID不可用”怎么办?

最近&#xff0c;很多用户在苹果社区反馈&#xff0c;iPhone升级iOS 16后Face ID不能用了&#xff0c;尝试重置Face ID时&#xff0c;系统会弹窗提示“面容ID不可用&#xff0c;稍后尝试设置面容ID。” 如果你的iPhone在没有摔落手机或是手机进水的情况下出现这个弹窗&#xff…

【uniapp小程序】路由跳转navigator传参封装

文章目录&#x1f34d;前言&#x1f34b;正文1、看官网1.1 navigator API 介绍1.2、路由跳转参数传递1.3、五种常见的跳转方式1.3.1 uni.navigateTo(OBJECT)1.3.2 uni.redirectTo(OBJECT)1.3.3 uni.reLaunch(OBJECT)1.3.4 uni.switchTab(OBJECT)1.3.5 uni.navigateBack(OBJECT)…

图的初识·基本概念

文章目录基本概念图有两种基本形式无向图的表示有向图的表示基本概念 图结构也是数据结构的一部分。而且还有一点小难。图是由多个结点链接而成的&#xff0c;但是一个结点可以同时连接多个其他结点&#xff0c;多个节点也可以同时指向一个节点。【多对多的关系】 图结构是任意…

如何从报表控件FastReport .NET中连接到 PostgreSQL 数据库?

FastReport.NET官方版下载 Fastreport是目前世界上主流的图表控件&#xff0c;具有超高性价比&#xff0c;以更具成本优势的价格&#xff0c;便能提供功能齐全的报表解决方案&#xff0c;连续三年蝉联全球文档创建组件和库的“ Top 50 Publishers”奖。 慧都科技是Fast Repor…

mysql索引类别和失效场景

首先&#xff0c;我们为什么要使用索引&#xff0c;索引有什么作用呢&#xff1f; 索引可以用来快速查询数据表中有某一特定值的记录&#xff0c;大大加快数据的查询速度&#xff1b;在列上创建了索引之后&#xff0c;查找数据时可以直接根据该列上的索引找到对应记录行的位置…

YOLO X 改进详解

YOLO X 主要改进&#xff1a; Anchor-Free: FCOSDecoupled detection headAdvanced label assigning strategy Network structure improvement Decoupled detection head 对比于YOLO V5, YOLO X 在detection head上有了改进。YOLO V5中&#xff0c;检测头是通过卷积同时预…

视频编解码 - RTP 与 RTCP

目录 RTP 实时传输协议 RTCP协议 将H264 RTP打包 RTP 实时传输协议 音视频数据传输&#xff0c;先将原始数据经过编码压缩后&#xff0c;将码流打包成一个个RTP包&#xff0c;再将码流传输到接收端。 打包的作用 接收端要正确地使用这些音视频编码数据&#xff0c;不仅仅需…

JaVers:自动化数据审计

在开发应用程序时&#xff0c;我们经常需要存储有关数据如何随时间变化的信息。此信息可用于更轻松地调试应用程序并满足设计要求。在本文中&#xff0c;我们将讨论 JaVers 工具&#xff0c;该工具允许您通过记录数据库实体状态的更改来自动执行此过程。 Javers如何工作&#x…

vue Router

Vue项目各文件含义 1.src文件夹 是我们真正敲代码的文件夹&#xff0c; 2.assets放静态资源 3.components放组件 4.App.vue主组件 5.main.js项目的入口文件 Vue Router 在router/index.js路由文件中配置路由&#xff0c;设置路由跳转规则 import Vue from vue import Vu…

android Framework 中用到了哪些跨进程通信方式?

文章目录Linux 有哪些跨进程的通信方式&#xff1f;管道本地 Socket共享内存信号Linux 有哪些跨进程的通信方式&#xff1f; Binder 机制是Android基于Linux的一种独特的IPC机制。我们常用的AMS&#xff0c;PMS 等都是通过Binder机制来完成跨进程通信的&#xff0c;那么除了Bin…

【并发】深入理解Java线程的底层原理

线程基础知识 线程与进程 进程 操作系统会以进程为单位&#xff0c;分配系统资源&#xff08;CPU时间片、内存等资源&#xff09;&#xff0c;进程是资源分配的最小单位。 当一个程序被运行&#xff0c;从磁盘加载这个程序的代码至内存&#xff0c;这时就开启了一个进程。 线…

使用 Mason 创建自己的 Flutter brick

使用 Mason 创建自己的 Flutter brick 原文 https://medium.com/gytworkz/create-your-own-flutter-brick-using-mason-7abc70d0324e 前言 谁不喜欢用最少的努力完成大部分事情呢&#xff1f;我当然知道! &#xff01;Mason 帮我完成了几个简单的步骤。 在本文中&#xff0c;我…

Redis——》数据类型:List(列表)

推荐链接&#xff1a; 总结——》【Java】 总结——》【Mysql】 总结——》【Redis】 总结——》【Spring】 总结——》【SpringBoot】 总结——》【MyBatis、MyBatis-Plus】 Redis——》数据类型&#xff1a;List&#xff08;列表&#xff09;一、简介…

复现黑客在后门中藏匿后门

PHP实现在后门中藏匿后门 在攻击渗透的时候会传入shell后门方便进行远控。其中的后门包括多种类型&#xff0c;大马是功能最全的直接提供了可视化的界面方便攻击者进行提权、扫描、上传等一系列的操作。 但有很多hacker不讲武德&#xff0c;在写好的大马中藏入自己的后门&…