JUC并发编程——CAS

简介: JUC并发编程——CAS

正文


一、什么是CAS


       由于JVM的synchronized重量级锁涉及操作系统内核态下互斥锁的使用,因此其线程阻塞和唤醒都涉及进程在用户态和内核态频繁的切换,导致重量级锁开销大,性能低。CAS,Compare And Swap比较并替换。CAS 操作包含三个操作数 —— 内存位置(V)、预期原值(E)新值(N)。 如果内存位置的值与预期原值相匹配,那么处理器会自动将该位置值更新为新值 。否则,处理器不做任何操作。无论哪种情况,它都会在 CAS 指令之前返回该 位置的值。CAS也称为自旋锁,在一个(死)循环[for(;;)]里不断进行CAS操作,直到成功为止(自旋操作),实际上,CAS也是一种乐观锁。


Unsafe 提供的CAS方法


public final native boolean compareAndSetReference(Object o, long offset,
                                                       Object expected,
                                                       Object x);
public final native boolean compareAndSetLong(Object o, long offset,
                                                  long expected,
                                                  long x);
public final native boolean compareAndSetLong(Object o, long offset,
                                                  long expected,
                                                  long x);


参数说明


o- 需要操作字段所在的对象


offset-需要操作字段的偏移量 相对对象头,在64位jvm虚拟机偏移量是12,因为markWord占64位,Class pointer(类对象指针)占32位,所以偏移量是12.


expected-期望值,也就是旧值


x-更新的值,也就是新值


在执行Unsafe的CAS方法时,这些方法值首先将内存位置的值与旧值比较,如果匹配那么CPU会自动将内存位置的值更新为新值,并返回true,如果不匹配,CPU不做任何操作,并返回false。当并发修改的线程少,冲突出现的机会少时,自旋次数也会减少,CAS的性能就会很高,反之冲突越多,自旋的次数越多,CAS的性能就会越低。


二、JUC原子类


以AtomicInteger为例


public final int get(); //获取当前的值
public final int getAndSet(int newValue); //获取当前的值,然后设置新的值
public final int getAndIncrement() ;//获取当前的值,然后自增
public final int getAndDecrement() ; //获取当前的值,然后自减
public final int getAndAdd(int delta) ; //获取当前的值,并加上预期的值
boolean compareAndSet(int expect, int update);//通过CAS方式设置整数值


public class AtomicIntegerDemo extends Thread {
    private AtomicInteger atomicInteger;
    AtomicIntegerDemo(AtomicInteger atomicInteger) {
        this.atomicInteger = atomicInteger;
    }
    @Override
    public void run() {
        for (int i = 0; i < 100000; i++) {
            atomicInteger.getAndIncrement();
        }
    }
    public static void main(String[] args) throws InterruptedException {
        AtomicInteger atomicInteger = new AtomicInteger(0);
        AtomicIntegerDemo demo1 = new AtomicIntegerDemo(atomicInteger);
        AtomicIntegerDemo demo2 = new AtomicIntegerDemo(atomicInteger);
        AtomicIntegerDemo demo3 = new AtomicIntegerDemo(atomicInteger);
        demo1.start();
        demo2.start();
        demo3.start();
        demo1.join();
        demo2.join();
        demo3.join();
        System.out.println(atomicInteger.get());//300000
    }
}


由上面执行结果可知AtomicInteger是一个原子的操作,并发操作是线程安全的。AtomicInteger主要通过CAS自旋+volatile的方案实现,既保障了变量操作的线程安全性,又避免了synchronized重量级锁的开销。


三、ABA问题


       使用CAS操作内存数据时,数据发生过变化也能更新成功,如A——>B——>A,最后一个CAS预期数据A实际已经发生过更改,但也能修改成功,这就产生了ABA的问题。ABA的解决思路一般是使用版本号,每次变更都带上版本号。JDK提供了两个类AtomicStampedReference和AtomicMarkableReference解决ABA的问题。


       AtomicStampedReference在CAS的基础上增加了一个Stamp(印戳或者标记),使用这个标识可以判断数据是否发生变化。


 public static void main(String[] args) {
        String str1 = "aaa";
        String str2 = "bbb";
        //初始化AtomicStampedReference 初始值是aaa,印戳是1
        AtomicStampedReference<String> reference = new AtomicStampedReference<String>(str1, 1);
        //cas比较str1=aaa ,reference.getStamp()=1,就把str2的值更新到新值,印戳加1
        reference.compareAndSet(str1, str2, reference.getStamp(), reference.getStamp() + 1);
        //当前的值为:bbb印戳是:2
        System.out.println("当前的值为:" + reference.getReference() + "印戳是:" + reference.getStamp());
        boolean flag = reference.weakCompareAndSet(str2, "ccc", 2, reference.getStamp() + 1);
        //更新后的值为:bbb是否更新成功:false,更新失败,因为印戳4和2比较不对
        System.out.println("更新后的值为:" + reference.getReference() + "是否更新成功:" + flag);
    }


       AtomicMarkableReference是AtomicStampedReference的简化版,不关心修改过几次,只关心是否修改过,标志属性是boolean类型,其值只记录是够修改过。


public static void main(String[] args) {
        AtomicMarkableReference<Integer> atomicMarkableReference = new AtomicMarkableReference<Integer>(1, true);
        boolean b = atomicMarkableReference.compareAndSet(1, 2, true, false);
        //是否更新成功true更新后的值:2更新后的标志 mark:false
        System.out.println("是否更新成功" + b + "更新后的值:" + atomicMarkableReference.getReference() + "更新后的标志 mark:" + atomicMarkableReference.isMarked());
        boolean b1 = atomicMarkableReference.compareAndSet(2, 3, true, false);
        //是否更新成功false更新后的值:2更新后的标志 mark:false
        System.out.println("是否更新成功" + b1 + "更新后的值:" + atomicMarkableReference.getReference() + "更新后的标志 mark:" + atomicMarkableReference.isMarked());
    }


总结:


1、操作系统层面的CAS是一条CPU原子指令(cmpxchg指令),由于该指令具有原子性,因此使用CAS不会造成数据不一致的问题。


2、使用CAS无锁编程步骤如下,获取字段中的期望值(旧值)与内存地址上的值作比较(没有修改之前的旧值),如果两个值相等,就把新值放在字段的内存地址上,失败则自旋。


3、并发线程越少,自旋越少CAS性能越高,反之并发线程越多,自旋的次数越多,CAS的性能就越低。


4、jdk中的原子类大都是使用CAS实现的。


5、解决ABA问题使用版本号,jdk中有两个类AtomicStampedReference和AtomicMarkableReference解决ABA的问题。


参考:


《JAVA高并发核心编程(卷2):多线程、锁、JMM、JUC、高并发设计》-尼恩编著

相关文章
|
移动开发 数据可视化 UED
从网页到应用:简易教程教你如何在线生成App
本文将介绍一种简便的方法,让您能够将网页封装成APP。通过这种技术,您可以将您的网页应用程序转化为移动应用程序,从而更好地满足用户的需求。无需编程知识,只需几个简单的步骤,即可轻松将您的网页转化为功能强大的应用程序。
xltd文件怎么打开?教你xltd格式文件打开方法
xltd文件怎么打开?教你xltd格式文件打开方法
2067 0
|
存储 分布式计算 Java
深入探索Java在云计算领域的应用与优势
深入探索Java在云计算领域的应用与优势
562 0
|
10月前
|
Python
pyqt6 制作一个颜色调节器 01
本文介绍了一个使用 PyQt 制作的颜色调节器,通过滑动滚动条或旋钮来调整 RGB 三色,实现颜色的微调。具体步骤包括:1. 设计 UI 页面;2. 分析颜色调整逻辑;3. 将数据反馈到 UI 页面。最终实现了颜色随滑块变化而实时更新的效果。
221 1
|
10月前
|
缓存 监控 JavaScript
Vue.js 框架下的性能优化策略与实践
Vue.js 框架下的性能优化策略与实践
|
12月前
|
弹性计算 前端开发 容器
【前端web入门第六天】02 flex布局
Flex布局是一种现代CSS布局模式,通过给父元素设置`display: flex`,其子元素可自动挤压或拉伸。它包含弹性容器和弹性盒子,主轴默认为水平方向,侧轴为垂直方向。主轴对齐方式由`justify-content`属性控制,侧轴对齐方式包括`align-items`(针对所有子元素)和`align-self`(针对单个子元素)。修改主轴方向使用`flex-direction`属性,`flex`属性用于控制子元素在主轴上的伸缩比例。此外,`flex-wrap`属性允许子元素换行,而`align-content`属性则定义多行对齐方式。
229 9
|
敏捷开发 测试技术 持续交付
阿里云云效产品使用合集之如何将项目数据迁移到另外一个账号
云效作为一款全面覆盖研发全生命周期管理的云端效能平台,致力于帮助企业实现高效协同、敏捷研发和持续交付。本合集收集整理了用户在使用云效过程中遇到的常见问题,问题涉及项目创建与管理、需求规划与迭代、代码托管与版本控制、自动化测试、持续集成与发布等方面。
|
存储 运维 Shell
Ansible自动化运维工具安装和基本使用
Ansible 是一款无代理的IT自动化工具,通过SSH连接目标主机执行配置管理、应用部署和云端管理任务。它使用YAML编写的Playbook定义任务,核心组件包括Playbook、模块、主机清单、变量等。Ansible的优势在于易用、功能强大、无须在目标主机安装额外软件,并且开源。安装过程涉及配置网络源、yum安装和SSH密钥设置。通过定义主机清单和使用模块进行通信测试,确保连接成功。
419 2
Ansible自动化运维工具安装和基本使用
|
搜索推荐 JavaScript 前端开发
【数字化销售】CPQ软件概述
【数字化销售】CPQ软件概述
|
存储 监控 安全
我们的服务:通信网络单元定级备案指南
通信网络单元定级备案是指相关基础电信企业、增值电信企业要对本单位管理、运行的公用通信网和互联网及其各类信息系统进行单元划分,按照《通信网络安全防护管理办法》(工业和信息化部令第11号)的规定开展定级工作,并在工业和信息化部“通信网络安全防护管理系统”报送各单元的定级信息。