CAS(Compare-and-Swap),即比较并替换,java并发包中许多Atomic的类的底层原理都是CAS。

它的功能是判断内存中某个地址的值是否为预期值,如果是就改变成新值,整个过程具有原子性。

具体体现于sun.misc.Unsafe类中的native方法,调用这些native方法,JVM会帮我们实现汇编指令,这些指令是CPU的原子指令,因此具有原子性。

 public class CASDemo {

     public static void main(String[] args) {

         //初始值5
AtomicInteger atomicInteger = new AtomicInteger(5); //和5比较,设置为10
System.out.println("预期值:5,当前值:"+atomicInteger);
System.out.println("是否设置成功:"+atomicInteger.compareAndSet(5, 10));
//和5比较,设置为15
System.out.println("预期值:5,当前值:"+atomicInteger);
System.out.println("是否设置成功:"+atomicInteger.compareAndSet(5, 15)); System.out.println("当前值:"+atomicInteger);
}
}

输出为:

预期值:5,当前值:5
是否设置成功:true
预期值:5,当前值:10
是否设置成功:false
当前值:10

下面看一下getAndAddInt在底层Unsafe类中的代码(自旋锁),运用到了CAS

//va1为对象,var2为地址值,var4是要增加的值,var5为当前地址中最新的值
public final int getAndAddInt(Object var1, long var2, int var4) {
int var5;
do {
var5 = this.getIntVolatile(var1, var2);
} while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4)); return var5;
}

首先通过volatile的可见性,取出当前地址中的值,作为期望值。如果期望值与实际值不符,就一直循环获取期望值,直到set成功。

适用场景:

  1. CAS 适合简单对象的操作,比如布尔值、整型值等;

  2. CAS 适合冲突较少的情况,如果太多线程在同时自旋,那么长时间循环会导致 CPU 开销很大;

CAS的缺点:

  1. CPU开销过大 : 在并发量比较高的情况下,如果许多线程反复尝试更新某一个变量,却又一直更新不成功,循环往复,会给CPU带来很到的压力。

  2. 不能保证代码块的原子性:CAS机制所保证的知识一个变量的原子性操作,而不能保证整个代码块的原子性。比如需要保证3个变量共同进行原子性的更新,就不得不使用synchronized了。

  3.  ABA问题:如果内存地址V初次读取的值是A,在CAS等待期间它的值曾经被改成了B,后来又被改回为A,那CAS操作就会误认为它从来没有被改变过。

ABA问题以及解决:使用带版本号的原子引用AtomicStampedRefence<V>,或者叫时间戳的原子引用,类似于乐观锁。

 0 // ABA问题及解决方式
1 public class ABADemo { private static AtomicReference<String> atomicReference = new AtomicReference<>("A");
private static AtomicStampedReference<String> stampReference = new AtomicStampedReference<>("A",1); public static void main(String[] args){
new Thread(()->{
//获取到版本号
int stamp = stampReference.getStamp();
System.out.println("t1获取到的版本号:"+stamp);
try {
//暂停1秒,确保t1,t2版本号相同
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
atomicReference.compareAndSet("A","B");
atomicReference.compareAndSet("B","A"); stampReference.compareAndSet("A","B",stamp,stamp+1);
stampReference.compareAndSet("B","A",stamp+1,stamp+2);
System.out.println("t1线程ABA之后的版本号:"+stampReference.getStamp()); },"t1").start(); new Thread(()->{
//获取到版本号
int stamp = stampReference.getStamp();
System.out.println("t2获取到的版本号:"+stamp);
try {
//暂停2秒,等待t1执行完成ABA
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.print("普通原子类无法解决ABA问题: ");
System.out.println(atomicReference.compareAndSet("A","C")+"\t"+atomicReference.get());
System.out.print("版本号的原子类解决ABA问题: ");
System.out.println(stampReference.compareAndSet("A","C",stamp,stamp+1)+"\t"+stampReference.getReference()); },"t2").start();
}
}

输出结果:普通原子引用类在另一个线程完成ABA之后继续修改(把A改成了C),带版本号原子引用有效的解决了这个问题。

t1获取到的版本号:1
t2获取到的版本号:1
t1线程ABA之后的版本号:3
普通原子类无法解决ABA问题: true C
版本号的原子类解决ABA问题: false A

CAS机制与自旋锁的更多相关文章

  1. 二、多线程基础-乐观锁_悲观锁_重入锁_读写锁_CAS无锁机制_自旋锁

    1.10乐观锁_悲观锁_重入锁_读写锁_CAS无锁机制_自旋锁1)乐观锁:就像它的名字一样,对于并发间操作产生的线程安全问题持乐观状态,乐观锁认为竞争不总是会发生,因此它不需要持有锁,将 比较-设置 ...

  2. 我们常说的 CAS 自旋锁是什么

    CAS(Compare and swap),即比较并交换,也是实现我们平时所说的自旋锁或乐观锁的核心操作. 它的实现很简单,就是用一个预期的值和内存值进行比较,如果两个值相等,就用预期的值替换内存值, ...

  3. 多线程之美6一CAS与自旋锁

    1.什么是CAS CAS 即 compare and swap 比较并交换, 涉及到三个参数,内存值V, 预期值A, 要更新为的值B, 拿着预期值A与内存值V比较,相等则符合预期,将内存值V更新为B, ...

  4. 线程安全之CAS机制详解(分析详细,通俗易懂)

    背景介绍:假设现在有一个线程共享的变量c=0,让两个线程分别对c进行c++操作100次,那么我们最后得到的结果是200吗? 1.在线程不安全的方式下:结果可能小于200,比如当前线程A取得c的值为3, ...

  5. 【C# 线程】并发编程的基石——CAS机制

    其实Java并发框架的基石一共有两块,一块是本文介绍的CAS,另一块就是AQS,后续也会写博客介绍. 什么是CAS机制 CAS机制是一种数据更新的方式.在具体讲什么是CAS机制之前,我们先来聊下在多线 ...

  6. linux自旋锁、互斥锁、信号量

    为了避免并发,防止竞争.内核提供了一组同步方法来提供对共享数据的保护. 我们的重点不是介绍这些方法的详细用法,而是强调为什么使用这些方法和它们之间的差别. Linux 使用的同步机制可以说从2.0到2 ...

  7. Linux内核同步:自旋锁

    linux内核--自旋锁的理解 自旋锁:如果内核配置为SMP系统,自旋锁就按SMP系统上的要求来实现真正的自旋等待,但是对于UP系统,自旋锁仅做抢占和中断操作,没有实现真正的“自旋”.如果配置了CON ...

  8. 菜鸟nginx源代码剖析数据结构篇(十) 自旋锁ngx_spinlock

    菜鸟nginx源代码剖析数据结构篇(十) 自旋锁ngx_spinlock Author:Echo Chen(陈斌) Email:chenb19870707@gmail.com Blog:Blog.cs ...

  9. 使用C++11原子量实现自旋锁

    一.自旋锁 自旋锁是一种基础的同步原语,用于保障对共享数据的互斥访问.与互斥锁的相比,在获取锁失败的时候不会使得线程阻塞而是一直自旋尝试获取锁.当线程等待自旋锁的时候,CPU不能做其他事情,而是一直处 ...

随机推荐

  1. JavaWeb(四)Servlet-1

    1.Servlet 简介 Java Servlet是和平台无关的服务器端组件,它运行在Servlet容器中.Servlet容器负责Servlet和客户的通信以及调用Servlet的方法,Servlet ...

  2. C#关于winform时间格式2017.05.27

    winform中的控件为DateTimePicker DateTime startdate = startTime.Value;//获取到Winform控件的时间 String start = sta ...

  3. Android 自定义 HorizontalScrollView 打造再多图片(控件)也不怕 OOM 的横向滑动效果

    转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/38140505 自从Gallery被谷歌废弃以后,Google推荐使用ViewPa ...

  4. 【最小生成树】UVA1494Qin Shi Huang's National Road System秦始皇修路

    Description During the Warring States Period of ancient China(476 BC to 221 BC), there were seven ki ...

  5. laravel 查询数据返回的结果

    laravel查询数据返回的结果 在插入数据库的时候,发现查询数据返回的结果是一个对象;即使是空数据 返回的不是true或者false 那么要判断该结果是否查询有结果 该如果呢? 学习源头: http ...

  6. Linux下可以ping ip地址但无法ping域名解决方法

    分析:当前系统无法解决域名至ip地址故障. 步骤阅读 2 三:解决过程: 1.分析dns故障: 2.物理机可以ping 地址,但无法ping域名: 3.检查/etc/resolv.conf: 注: ( ...

  7. 好几个div(元素)找到最后一个

    <div> <div></div> <div></div> <div></div> </div> //找 ...

  8. SpringBoot---页面跳转之WebMvcConfigurerAdapter

    摘要:在springboot中定义自己的方法继承WebMvcConfigurerAdapter方法可以实现扩展springMvc功能,要全面实现接管springmvc就要在自己的方法上加上@Enabl ...

  9. 如何将markdown转换为wxml

    话说我要为技术博客写一个小程序版,我的博客解决方案是 hexo + github-page,格式当然是技术控们喜欢的 markdown 了 .但小程序使用的却是独有的模版语言 WXML.我总不能把之前 ...

  10. java游戏开发杂谈 - 有限状态机

    在不同的阶段,游戏所运行的逻辑.所显示的界面,都是不同的. 以五子棋举例,游戏开始.游戏中.胜负已分,对应的界面和逻辑都不同. 在游戏中,又分为:自己下棋.对方下棋.游戏暂停.悔棋等多个状态. 再比如 ...