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. JS 总结----基础部分

    javascript基础:1.每个属性都可以加ID2.写函数的时候注意 == 和 = 不要写错了3.html里面怎么写js里面就怎么写,但是有一例外 className 比如oDiv.class = ...

  2. 关于JQuery的技巧、易错点(连载中.....)

    JQuery的诞生让我们对原生态的js代码变得陌生起来,不得不说,他真的是很强大,接下来博主就浅谈一下我对JQuery的一些认知和小tips. JQuery:他是一个JavaScript库,他将原生态 ...

  3. volume_manager.go

    package ) type) ],,) ,)             vms.CanCreateVolume = false         } else {             vms.Can ...

  4. req_params.go

    , fmt.Sprintf("%s: closing %s", proto, listener.Addr())) }

  5. go语言nsq源码解读八 http.go、http_server.go

    这篇讲另两个文件http.go.http_server.go,这两个文件和第六讲go语言nsq源码解读六 tcp.go.tcp_server.go里的两个文件是相对应的.那两个文件用于处理tcp请求, ...

  6. 【Floyd】BZOJ1491: [NOI2007]社交网络

    Description   Solution n<=100自然联想Floyd 设两个数组d[n][n]存最短距离,t[n][n]存最短路径条数 更新d的时候顺便更新t,乘法原理 if(d[i][ ...

  7. AbstractQueuedSynchronizer AQS框架源码剖析

    一.引子 Java.util.concurrent包都是Doug Lea写的,来混个眼熟 是的,就是他,提出了JSR166(Java Specification RequestsJava 规范提案), ...

  8. 关于throw、throws、try--catch的问题

    首先回顾概念 throws表示出现异常的一种可能性,并不一定会发生这些异常 throw则是抛出了异常,执行throw则一定抛出了某种异常 try--catch try语句用大括号{}指定了一段代码,该 ...

  9. String去重方法

    思路:利用集合的contains方法将某个字符串中的集合中没有的单个字符添加到集合中,然后再将集合中每个元素做拼接 @Test public void aa5(){ String aa="a ...

  10. python设计模式-观察者

    定义: 定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖的对象都会得到通知并被自动更新. 观察者模式是对象的行为模式,又叫发布-订阅(pubish/subscribe)模式,模型 ...