首页 > 编程语言 >Java的CAS操作

Java的CAS操作

时间:2023-05-21 11:06:52浏览次数:34  
标签:Java 变量 CAS 线程 写回 操作

介绍 CAS

技术是为了解决问题而生的,通过 CAS 我们可以以无锁的方式,保证对共享数据进行 “读取 - 修改 - 写回” 操作序列的正确性。

CAS 是乐观锁设计思想的实现。CAS 的思想是:在“读取 - 修改 - 写回”操作序列中,先读取并修改数据,写回数据前先判断读取数据后的这段时间内数据是否发生变化(共享变量的当前值是否是我们的期望值):

  • 如果在此期间数据没有发生变化(共享资源的当前值是我们的期望值),那么就把修改后的值写回
  • 如果在此期间其他的线程修改了数据,数据发生了变化(共享资源的当前值不是我们的期望值),那么就放弃本次写回操作,再基于最新的数据进行修改然后重试,避免发生数据更新丢失

CAS 更加底层的实现依赖于 CPU 提供的特定指令,具体根据体系结构的不同还存在着明显区别。比如,x86 CPU 提供 cmpxchg 指令;而在精简指令集的体系架构中,则通常是靠一对指令(如“load and reserve”和“store conditional”)实现的。在大多数处理器上 CAS 都是非常轻量级的操作,这也是其优势所在。

Java 的 CAS 操作

CAS 依赖于 Unsafe 类提供的一些底层能力,进行底层操作。

/**
 * Atomically update Java variable to x if it is currently
 * holding expected.
 * @return true if successful
 */
public final native boolean compareAndSwapInt(Object o, long offset,
                                              int expected,
                                              int x);

在调用 compareAndSwap() 方法时,我们需要传入需要修改的共享变量、对象偏移量、我们期望的变量当前值、要写回的值。如果变量的当前值和我们的期望值相等,则写回成功,返回 true,否则写回失败,返回 false。

Unsafe 类是 Java 提供的一个操作内存的非安全类,操作对象和对应的变量来完成 CAS 操作。显然 Unsafe 类过于底层,调用 Unsafe 类的方法不是大多数应用场景的最好选择。目前 Java 提供了两种公共 API,可以实现 CAS 操作:

  • 一种是 Atomic 原子类。Atomic 包中的类对 Unsafe 类进行了封装,使我们可以更方便的使用 CAS 操作。Atomic 包提供了常见的原子性数据类型,甚至是引用、数组等相关原子类型和原子更新操作工具。
  • 还有一种是 Variable Handle API,它源自于JEP 193,提供了各种粒度的原子或者有序性的操作等。

CAS 的优劣局限

CAS 的优点:在大多数处理器上 CAS 都是非常轻量级的操作。


CAS 的局限:

  • CAS 操作是针对一个共享变量的,如果需要解决多个变量的原子性问题,建议还是使用互斥锁方案。
  • 存在 ABA 问题:当一个线程在进行 CAS 操作时,另一个线程可能会在此期间修改了同一个共享变量的值,然后又将其改回原来的值。这种情况下,CAS 操作就无法检测到共享变量值的变化,从而导致 ABA 问题。如果我们仅仅在写回数据前判断数值是 A,可能导致不合理的写回操作。针对这种情况,Java 提供了 AtomicStampedReference 工具类,通过为对象引用建立类似版本号(stamp)的方式,来解决 ABA 问题,保证 CAS 的正确性。
  • 如果有大量的线程同时对一个共享变量进行 CAS 操作,竞争过于激烈的情况下,尝试进行 CAS 操作的线程只会白白消耗处理器资源,而不会做任何有价值的工作,这就会带来性能的浪费。

标签:Java,变量,CAS,线程,写回,操作
From: https://blog.51cto.com/haofeiyu/6318729

相关文章

  • Java二叉树详解
    当你看到一棵茂盛的大树时,你是否曾想过这样的问题:它是如何生长起来的?落叶归根,数百年来,不断地生长与死亡。其实,每个程序员也可以成为一棵大树的缔造者。而Java的二叉树,就像互联网上的知识一样,通过它的枝干和叶子,能够让我们更加高效地搜索、插入和删除节点。虽然二叉树算法并不简单......
  • Java 拦截器和过滤器
    1.java后端基于过滤器或拦截器处理跨域请求 Java中的过滤器和拦截器的区别Java中的拦截器和过滤器有什么区别 springmvc执行流程? Action就是用于处理一次用户请求的对象。什么是Servlet容器? Servlet 容器只不过是一个Java程序。 ......
  • JavaScript基础知识笔记
    JavaScript是属于Web的编程语言,对网页行为进行编程。参考教程:https://www.w3school.com.cn/js/index.asphttps://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Language_overviewJavaScript能够改变HTML内容document.getElementById("demo").innerHTML="Hell......
  • Python操作常用数据库
    Python操作常用数据库前言简介SQLite连接SQLite创建数据表增删改查MySQLmysql-connector操作MySQL创建数据表增删改查pymysql操作MySQLMongoDB连接MongoDB增删改查Redis连接Redis前言本文是个人使用Python操作常用数据库的学习笔记,由于水平有限,难免出现错漏,敬请批评改正。简介数据......
  • java-求余数计算
    publicclassTest521929{  publicstaticvoidmain(String[]args){    System.out.println(100%3); //1     //算术表达式2个参数为整数,返回结果也是整数    System.out.println(100%3.0); //1.0    System.out.println(100.......
  • 在java中使用lua脚本操作redis
    前言众所周知,redis可以执行lua脚本,至于为什么要用lua脚本来操作redis,自行百度咯开始Bean类packagecn.daenx.myadmin.common.config.redis;importorg.springframework.context.annotation.Bean;importorg.springframework.context.annotation.Configuration;importorg.......
  • Java-Day-23( 线程二:线程终止 + 常用方法 + 用户线程 + 守护线程 + 线程生命周期 + Syn
    Java-Day-23线程终止setLoop()基本说明当线程完成任务后,会自动退出还可以通过使用变量来控制run方法退出的方式停止线程,即通知方式练习使用publicclasstest1{publicstaticvoidmain(String[]args)throwsInterruptedException{Tt1=newT();......
  • Java 网络编程 —— 实现非阻塞式的客户端
    创建阻塞的EchoClient客户程序一般不需要同时建立与服务器的多个连接,因此用一个线程,按照阻塞模式运行就能满足需求publicclassEchoClient{privateSocketChannelsocketChannel=null;publicEchoClient()throwsIOException{socketChannel......
  • 关于Java接口实现问题
    publicinterfaceInterfaceClass{/***jdk1.7只能有抽象方法,子类是**抽象类**时,方法就可以实现也可以不实现*/publicabstractvoidmethod();/***jdk1.8新增静态方法,默认方法**静态方法子类不能实现*/publicstaticvoidmethod1(){}/**......
  • golang操作redis
    首先,基于docker查看redis镜像dockerps-a然后执行dockerrun-p6379:6379-dredis:latestredis-server,将端口映射到本机最后执行dockerexec-ti13e638ea1036redis-cli-h0.0.0.0-p6379执行链接操作......