一、简介

CAS机制:(Compare and set)比较和替换

  简单来说–>使用一个期望值来和当前变量的值进行比较,如果当前的变量值与我们期望的值相等,就用一个新的值来更新当前变量的值
CAS有三个操作数:内存值V、旧的预期值A、要修改的值B,当且仅当预期值A和内存值V相同时(条件),将内存值修改为B并返回true,否则条件不符合返回false。条件不符合说明该变量已经被其它线程更新了。

  当多个线程访问相同的数据时,如果使用锁来进行并发控制,当某一个线程(T1)抢占到锁之后,那么其他线程再尝试去抢占锁时就会被阻塞,当T1释放锁之后,下一个线程(T2)再抢占到锁后并且重新恢复到原来的状态线程从阻塞到重新准备运行有很长的时间开销。而假设我们业务代码本身并不具备很复杂的操作,并发量也不高,那么当我们使用CAS机制来代替加锁操作,当多个线程操作同一变量时每个线程会首先会读取到地址值对应的原值作为自己的期望值,然后进行操作,操作完以后在更新的时候他会判断现在地址值对应的值是否与自己的期望值相同,如果相同,就认为自己操作的过程中别人没有进行过操作。则将自己操作后的值更新到地址值对应的位置,如果不同则说明自己操作的过程中一定有其他的线程更新过数据,然后会把当前地址值对应的值返回给用户,用户拿到新值重新上次的操作。不断循环直到更新成功。

二、用途

CAS的使用场景:juc下ReentryLock 和 Atomic类操作

CAS乐观锁(循环内自旋):原理:A=内存值,thread1对A进行累加操作后的值为B。更新内存值时会判断A和B是否相等,如果相等,那么B替换A退出循环,如果不相等,重新获得内存值,进行操作。此套流程如何保证内存值是最新的?详见volatile原理此套流程如何保证V,A比较B替换V时是原子操作?cas底层用unsafe直接访问底层操作系统,做了硬件级别的原子操作。

AtomicInteger源码分析
java.util.concurrent.atomic包下的原子操作类都是基于CAS实现的,接下去我们通过AtomicInteger来看看是如何通过CAS实现原子操作的:

public class AtomicInteger extends Number implements java.io.Serializable {
private static final long serialVersionUID = 6214790243416807050L;
// setup to use Unsafe.compareAndSwapInt for updates
//获得unsafe实例
private static final Unsafe unsafe = Unsafe.getUnsafe();
//存放变量value的内存偏移
private static final long valueOffset; static {
try {
//通过unsafe获得value的内存偏移
valueOffset = unsafe.objectFieldOffset
(AtomicInteger.class.getDeclaredField("value"));
} catch (Exception ex) { throw new Error(ex); }
} //volatile修饰的,保证了多线程之间看到的value值是同一份,后面会分析
private volatile int value;

接下来看看设置新的值是如何完成的:

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

这里很简单,直接调用unsafe的cas方法就可以了,通过value的内存偏移,以及期望的值来设置新的值。接下来就是本地方法调用了。

 public final int getAndIncrement() {
return unsafe.getAndAddInt(this, valueOffset, 1);
}

同样原子增加的操作就是通过unsafe来完成,只是每次都是增加1而已。
可以去看看其他几个原子操作类:AtomicLong(原子更新长整型)AtomicBoolean(原子更新布尔类型,用int来实现的),AtomicIntegerArray(原子更新整型数组里的元素),AtomicReference(原子更新引用类型)等,其核心思想都是用unsafe提供的原子操作来完成的。
在Java并发编程的艺术中,作者实现了一个基于CAS线程安全的计数器和一个非线程安全的计数器,本质就是用原子操作类代替一般的int或者Long数据类型,通过原子操作类来完成原子操作,保证了计数的线程安全,但是这里提一下,原子操作类不是什么时候都是线程安全的,当由原子操作类来完成复合操作是,此时就不一定是线程安全的了。

 AtomicInteger a=new AtomicInteger(10);
//假设下面会出现线程竞争
int b=a.get();
if (b==10){
a.compareAndSet(10,100);
}

在多线程中,这样就不是线程安全的了,因为先取出某个值,然后在判断,这整个操作不是原子操作。


三、优缺点

cas优点:

  如一描述在并发量不是很高时cas机制会提高效率。

cas缺点:

1.循环时间开销太大:

  如果CAS长时间执行不成功,则会给CPU带来交大的执行开销。处理器提供一种pause指令可以缓解这部分问题,pause指令有两个作用,第一它可以延迟流水线执行指令(de-pipeline),使CPU不会消耗过多的执行资源,延迟的时间取决于具体实现的版本,在一些处理器上延迟时间是零。第二它可以避免在退出循环的时候因内存顺序冲突(memory order violation)而引起CPU流水线被清空(CPU pipeline flush),从而提高CPU的执行效率。

2。只能保证一个共享变量的原子操作。

  如果需要对多个共享变量进行同步,就得使用锁,或者将几个共享变量封装起来,使用CAS来进行同步。从Java1.5开始JDK提供了AtomicReference类来保证引用对象之间的原子性,你可以把多个变量放在一个对象里来进行CAS操作

3、ABA问题

aba问题:内存值V=100;
threadA 将100,改为50;
threadB 将100,改为50;
threadC 将50,改为100;

场景:小牛取款,由于机器不太好使,多点了几次全款操作。后台threadA和threadB工作,
此时threadA操作成功(100->50),threadB阻塞。正好牛妈打款50元给小牛(50->100),
threadC执行成功,之后threadB运行了,又改为(100->50)。
牛气冲天,lz钱哪去了???

如何解决aba问题:对内存中的值加个版本号,在比较的时候除了比较值还的比较版本号。

java:AtomicStampedReference就是用版本号实现cas机制。

CAS机制总结的更多相关文章

  1. 深入浅出Java并发包—CAS机制

    在JDK1.5之前.Java主要靠synchronized这个关键字保证同步,已解决多线程下的线程不安全问题,但是这会导致锁的发生,会引发一些个性能问题. 锁主要存在一下问题 (1)在多线程竞争下,加 ...

  2. CAS机制与自旋锁

    CAS(Compare-and-Swap),即比较并替换,java并发包中许多Atomic的类的底层原理都是CAS. 它的功能是判断内存中某个地址的值是否为预期值,如果是就改变成新值,整个过程具有原子 ...

  3. 什么是CAS机制?(转)

    围绕下面四个点展开叙述: 一:什么是CAS机制? 二:Java当中CAS的底层实现 三:CAS的ABA问题和解决方法 四:java8对CAS的优化 一:什么是CAS机制? 我们先看一段代码: 启动两个 ...

  4. Java CAS同步机制 原理详解(为什么并发环境下的COUNT自增操作不安全): Atomic原子类底层用的不是传统意义的锁机制,而是无锁化的CAS机制,通过CAS机制保证多线程修改一个数值的安全性。

    精彩理解:  https://www.jianshu.com/p/21be831e851e ;  https://blog.csdn.net/heyutao007/article/details/19 ...

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

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

  6. (白话理解)CAS机制

    (白话理解)CAS机制 通过一段对话我们来了解cas用意 示例程序:启动两个线程,每个线程中让静态变量count循环累加100次. 最终输出的count结果是什么呢?一定会是200吗? 加了同步锁之后 ...

  7. 并发之atomicInteger与CAS机制

    并发之atomic与CAS自旋锁 通过前几章的讲解我们知道i++这种类似操作是不安全的.针对这种情况,我们可能会想到利用synchronize关键字实现线程同步,保证++操作的原子性,的确这是一种有效 ...

  8. 对CAS机制的理解(一)

    先看一段代码:启动两个线程,每个线程中让静态变量count循环累加100次. public class CountTest { public static int count = 0; public ...

  9. 对CAS机制的理解(二)

    一.Java当中CAS的底层实现首先看看AtomicInteger的源码,AtomicInteger中常用的自增方法 incrementAndGet: public final int increme ...

  10. 详解java中CAS机制所导致的问题以及解决——内存顺序冲突

    [CAS机制] 指的是CompareAndSwap或CompareAndSet,是一个原子操作,实现此机制的原子类记录着当前值的在内存中存储的偏移地址,将内存中的真实值V与旧的预期值A做比较,如果不一 ...

随机推荐

  1. BZOJ2843:极地旅行社(LCT入门题)

    不久之前,Mirko建立了一个旅行社,名叫“极地之梦”.这家旅行社在北极附近购买了N座冰岛,并且提供观光服 务.当地最受欢迎的当然是帝企鹅了,这些小家伙经常成群结队的游走在各个冰岛之间.Mirko的旅 ...

  2. CSYZDay2模拟题解

    T1.rotate [问题描述] ZYL有N张牌编号分别为1, 2,……,N.他把这N张牌打乱排成一排,然后他要做一次旋转使得旋转后固定点尽可能多.如果第i个位置的牌的编号为i,我们就称之为固定点.旋 ...

  3. Consistent Hashing算法

    前几天看了一下Memcached,看到Memcached的分布式算法时,知道了一种Consistent Hashing的哈希算法,上网搜了一下,大致了解了一下这个算法,做下记录. 数据均衡分布技术在分 ...

  4. hdu5521 ( Dijkstra )

    题目描述: 给定一张图,问从1和n相遇的最短时间. 这道题的输入比较特殊,不能直接存,所以怎么输入,怎么存取,只要可以访问到一个节点的相邻节点就可以,由于spfa算法的时间复杂度为m*n,而Dijks ...

  5. hdu3555(数位DP dfs/递推)

    Bomb Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 131072/65536 K (Java/Others)Total Submi ...

  6. 查看html元素绑定的事件与方法 visual Event 插件

    WEB标准提倡结构.表现和行为相 分离,现在越来越多采用这种表现和行为的方式,但它也为我们开发调试带来一些问题,网页载入一堆JavaScript,,我们很难搞清楚最后在哪些元素的哪个动作绑定了事件,尤 ...

  7. linux下解压tgz文件(转载)

    转自:http://www.blogjava.net/chenlb/archive/2008/09/03/226654.html .tgz 解压:    tar zxvf myfile.tgz

  8. bzoj 4247: 挂饰【dp】

    bzoj上访问负下标会跑到奇怪的地方-- 其实可以滚动数组优化,但是我看能过就懒得改了 设f[i][j]为已经算了前i个挂饰,当前有j个空的钩子,转移就是f[i][j]=max(f[i-1][j],f ...

  9. 洛谷P4116 Qtree3(树剖+线段树)

    传送门 LCT秒天秒地 树剖比较裸的题了 用线段树记录一下区间的最左边的黑点的编号(因为同一条链上肯定是最左边的深度最小,到根节点距离最近) 然后记得树剖的时候肯定是越后面的答案越优,因为深度越浅 / ...

  10. jQuery笔记之Easing Plugin

    jQuery easing 使用方法首先,项目中如果需要使用特殊的动画效果,则需要在引入jQuery之后引入jquery.easing.1.3.js<script type="text ...