CAS解析
Posted 敲代码的小小酥
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了CAS解析相关的知识,希望对你有一定的参考价值。
CAS
概念
CAS是支持并发的第一个处理器提供原子的测试并设置操作,通常在单位上运行这项操作。操作数为V,A,B。
处理器(包括 Intel 和 Sparc 处理器)使用的最通用的方法是实现名为 比较并转换或 CAS 的原语。(在 Intel 处理器中,比较并交换通过指令的 cmpxchg 系列实现。PowerPC 处理器有一对名为“加载并保留”和“条件存储”的指令,它们实现相同的目地;MIPS 与 PowerPC 处理器相似,除了第一个指令称为“加载链接”。)
CAS 操作包含三个操作数 —— 内存位置(V)、预期原值(A)和新值(B)。如果内存位置的值与预期原值相匹配,那么处理器会自动将该位置值更新为新值。否则,处理器不做任何操作。无论哪种情况,它都会在 CAS 指令之前返回该位置的值。(在 CAS 的一些特殊情况下将仅返回 CAS 是否成功,而不提取当前值。)CAS 有效地说明了“我认为位置 V 应该包含值 A;如果包含该值,则将 B 放到这个位置;否则,不要更改该位置,只告诉我这个位置的值即可。
通常将 CAS 用于同步的方式是从地址 V 读取值 A,执行多步计算来获得新值 B,然后使用 CAS 将 V 的值从 A 改为 B。如果 V 处的值尚未同时更改,则 CAS 操作成功。
类似于 CAS 的指令允许算法执行读-修改-写操作,而无需害怕其他线程同时修改变量,因为如果其他线程修改变量,那么 CAS 会检测它(并失败),算法可以对该操作重新计算。清单 3 说明了 CAS 操作的行为(而不是性能特征),但是 CAS 的价值是它可以在硬件中实现,并且是极轻量级的(在大多数处理器中)。
基本原理
采用了乐观锁思想,首先线程获取到共享资源变量的值,然后再计算出要赋予共享变量的新值,最后,调用硬件处理器的CAS机制,保证原子操作:首先比较内存中共享变量现在的值是否与线程中存入的共享变量的旧值相等,如果相等,则将新值赋予共享变量,赋值成功,这个过程是原子性的,由底层处理器完成,在这期间共享变量值不会被其他线程所修改。
如果比较内存中值与线程存的旧值不相等,说明已经被其他线程所修改过了,此时修改失败,需要采用自旋机制,重新获取到最新的共享变量的值,然后重新走一遍CAS流程。
Unsafe类
jdk的CAS机制有Unsafe类调用底层操作系统所实现,相关方法如下:
public final native boolean compareAndSwapObject(Object var1, long var2, Object var4, Object var5);
public final native boolean compareAndSwapInt(Object var1, long var2, int var4, int var5);
public final native boolean compareAndSwapLong(Object var1, long var2, long var4, long var6);
Unsafe类还有很多操作底层的功能,如操作系统的内存分配等等。但是Unsafe构造方法是私有的,所以只能通过反射获取。有关Unsafe类的其他作用和详解,可参考文章:java中的Unsafe
CAS存在问题
ABA问题
如果一个变量初次读取的时候是A值,并且在准备赋值的时候检查到它仍然是A值,那我们就能说明它的值没有被其它线程修改过了吗?如果这段期间它的值曾经被改成了B,然后又改回A,那么CAS操作就会误认为它从来没有被修改过。这个漏洞称为CAS操作的ABA问题。java.util.concurrent包为了解决这个问题,提供了一个带有标记性的原子引用类AtomicStampedRenference,它可以通过控制变量值的版本来保证CAS的正确性。不过目前来说这个类比较鸡肋,大部分情况下ABA问题并不会影响程序并发的正确性,如果需要解决ABA问题,使用传统的互斥同步可能回避原子类更加高效。
有限性
一次CAS指令只能控制一个变量,不能控制多个变量,如果想保证多个变量的安全性,则需要多个CAS指令,或者将多个变量组成一个对象,将对象进行CAS操作。
效率问题
效率问题:前面提到,如果存在多个线程竞争,可能导致CAS失败,此时可能需要循环(自旋)执行CAS,竞争激烈情况下会对性能有一定影响;
Atomic相关类解析
Atomic类采用CAS机制,保证并发安全,下面以AtomicInteger为例,分析使用和原理:
//原子设置为给定值并返回旧值。
public final int getAndSet(int newValue)
return unsafe.getAndSetInt(this, valueOffset, newValue);
//如果当前值==预期值,则原子地将该值设置为给定的更新值。
public final boolean compareAndSet(int expect, int update)
return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
//当前值原子递增一
public final int getAndIncrement()
return unsafe.getAndAddInt(this, valueOffset, 1);
//原子地将当前值减少一
public final int getAndDecrement()
return unsafe.getAndAddInt(this, valueOffset, -1);
等等一系列原子操作,都是利用了CAS原理。保证在多线程环境下,对一个变量的操作保证安全性。
其他相关原子操作类:
以上是关于CAS解析的主要内容,如果未能解决你的问题,请参考以下文章
CAS(Compare and swap)比较并交换算法解析
CAS(Compare and swap)比较并交换算法解析