并发编程CAS操作
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
并发编程CAS操作
并发编程CAS操作
简介
CAS即compare and swap,中⽂就是⽐较并交换
CAS是Java并发包的基⽯
原理
其实CAS的原理相对来说⽐较简单。
将要被改变的数据和期望的值作⽐较,当两个值相等时,再将数值替换成新值。
其实通俗的来讲就是"我认为原有的值是什么样⼦,如果⼀样则将原有的值更换成新值,否则不做修改,并返回原有的值"。
这⼀系列操作是原⼦的。
CAS 指的是现代CPU⼴泛⽀持的⼀种对内存中的共享数据进⾏操作的⼀种特殊指令
CAS有3个操作数,内存值V,旧的预期值A,要修改的新值B。
当且仅当预期值A和内存值V相同时,将内存值V修改为B,否则返回V。
这是⼀种乐观锁的思路
使⽤CAS可以实现⾮阻塞⽆锁的⽅式实现原⼦操作
实例
在java并发包中有java.util.concurrent.atomic这样⼀个包,该包是对Java部分数据类型的原⼦封装,通过CAS提供了原⼦性的操作⽅法,保证了线程安全。
如下代码
public class AtomicInteger extends Number implements java.io.Serializable {
private static final long serialVersionUID = 6214790243416807050L;
// 使⽤Unsafe包来实现CAS操作
private static final Unsafe unsafe = Unsafe.getUnsafe();
private static final long valueOffset;
static {
try {
valueOffset = unsafe.objectFieldOffset
(AtomicInteger.class.getDeclaredField("value"));
} catch (Exception ex) { throw new Error(ex); }
}
//使⽤volatile修饰符保证值value的线程可见性
private volatile int value;
/**
* Creates a new AtomicInteger with the given initial value.
*
* @param initialValue the initial value
*/
public AtomicInteger(int initialValue) {
value = initialValue;
}
/**
* Creates a new AtomicInteger with initial value {@code 0}.
*/
public AtomicInteger() {
/**
* 获得当前值
*
* @return the current value
*/
public final int get() {
return value;
}
/**
* 设置值
*
* @param newValue the new value
*/
public final void set(int newValue) {
value = newValue;
}
/**
* Eventually sets to the given value.
*
* @param newValue the new value
* @since 1.6
*/
public final void lazySet(int newValue) {
unsafe.putOrderedInt(this, valueOffset, newValue);
}
/**
* 原⼦性的设置新值,返回新值
*
* @param newValue the new value
* @return the previous value
*/
public final int getAndSet(int newValue) {
for (;;) {
int current = get();
if (compareAndSet(current, newValue))
return current;
}
}
/**
* ⽐较内存中的值和预期值,如果相同则更新,否则不进⾏操作
*
* @param expect the expected value
* @param update the new value
* @return true if successful. False return indicates that
* the actual value was not equal to the expected value.
*/
public final boolean compareAndSet(int expect, int update) {
return pareAndSwapInt(this, valueOffset, expect, update); }
/**
* Atomically sets the value to the given updated value
* if the current value {@code ==} the expected value.
* <p>May <a href="package-summary.html#Spurious">fail spuriously</a> * and does not provide ordering guarantees, so is only rarely an
* appropriate alternative to {@code compareAndSet}.
*
* @param expect the expected value
* @param update the new value
* @return true if successful.
*/
public final boolean weakCompareAndSet(int expect, int update) {
return pareAndSwapInt(this, valueOffset, expect, update); }
/**
* 原⼦性的增加1,并返回当前值.
*
* @return the previous value
*/
public final int getAndIncrement() {
for (;;) {
int current = get();
int next = current + 1;
if (compareAndSet(current, next))
return current;
}
}
/**
* 原⼦性的减少1,并返回当前值.
*
* @return the previous value
*/
public final int getAndDecrement() {
for (;;) {
int current = get();
int next = current - 1;
if (compareAndSet(current, next))
return current;
}
}
/**
* 原⼦性的增加给定的值,并返回当前值.
*
* @param delta the value to add
* @return the previous value
*/
public final int getAndAdd(int delta) {
for (;;) {
int current = get();
int next = current + delta;
if (compareAndSet(current, next))
return current;
}
}
/**
* 原⼦性的增加1,并返回更新后的值.
*
* @return the updated value
*/
public final int incrementAndGet() {
for (;;) {
int current = get();
int next = current + 1;
if (compareAndSet(current, next))
return next;
}
}
/**
* 原⼦性的减少1,并放回更新后的值.
*
* @return the updated value
*/
public final int decrementAndGet() {
for (;;) {
int current = get();
int next = current - 1;
if (compareAndSet(current, next))
return next;
}
}
/**
* 原⼦性的增加给定的值,并返回更新后的值.
*
* @param delta the value to add
* @return the updated value
*/
public final int addAndGet(int delta) {
for (;;) {
int current = get();
int next = current + delta;
if (compareAndSet(current, next))
return next;
}
}
/**
* Returns the String representation of the current value. * @return the String representation of the current value. */
public String toString() {
return Integer.toString(get());
}
public int intValue() {
return get();
}
public long longValue() {
return (long)get();
}
public float floatValue() {
return (float)get();
}
public double doubleValue() {
return (double)get();
}
}
CAS的缺点
CAS的ABA问题
问题描述
线程p1在共享变量中读取到值A
p1被抢占,进程p2执⾏
p2把共享变量⾥的值从A改成了B,再改回到A,此时被p1抢占。
p1回来看到共享变量⾥的值没有被改变,于是继续执⾏。
维基百科上的例⼦
你拿着⼀个装满钱的⼿提箱在飞机场,此时过来了⼀个⽕辣性感的美⼥,然后她很暖昧地挑逗着你,并趁你不
注意的时候,把⽤⼀个⼀模⼀样的⼿提箱和你那装满钱的箱⼦调了个包,然后就离开了,你看到你的⼿提箱还
在那,于是就提着⼿提箱去赶飞机去了
CAS的ABA的解决办法
ABA问题的解决思路就是使⽤版本号。
在变量前⾯追加上版本号,每次变量更新的时候把版本号加⼀
从Java1.5开始JDK的atomic包⾥提供了⼀个类AtomicStampedReference来解决ABA问题。
这个类的compareAndSet⽅法作⽤是⾸先检查当前引⽤是否等于预期引⽤,并且当前标志是否等于预期标志,如果全部相等,则以原⼦⽅式将该引⽤和该标志的值设置为给定的更新值
循环时间长开销⼤
⾃旋CAS如果长时间不成功,会给CPU带来⾮常⼤的执⾏开销。
如果JVM能⽀持处理器提供的pause指令那么效率会有⼀定的提升,pause指令有两个作⽤,第⼀它可以延迟流⽔线执⾏指令(de-pipeline),使CPU不会消耗过多的执⾏资源,延迟的时间取决于具体实现的版本,在⼀些处理器上延迟时间是零。
第⼆它可以避免在退出循环的时候因内存顺序冲突(memory order
violation)⽽引起CPU流⽔线被清空(CPU pipeline flush),从⽽提⾼CPU的执⾏效率
只能保证⼀个共享变量的原⼦操作
当对⼀个共享变量执⾏操作时,我们可以使⽤循环CAS的⽅式来保证原⼦操作,但是对多个共享变量操作时,循环CAS就⽆法保证操作的原⼦性,这个时候就可以⽤锁,或者有⼀个取巧的办法,就是把多个共享变量合并成⼀个共享变量来操作。
⽐如有两个共享变量i=2,j=a,合并⼀下ij=2a,然后⽤CAS来操作ij。
从Java1.5开始JDK提供了AtomicReference类来保证引⽤对象之间的原⼦性,你可以把多个变量放在⼀个对象⾥来进⾏CAS操作。