标签:cat imp fse val 指令 uri 获取 判断 cep
CAS,Compare and Swap即比较并替换,设计并发算法时常用到的一种技术,Doug lea大神在java同步器中大量使用了CAS技术,鬼斧神工的实现了多线程执行的安全性。
目前的处理器基本都支持CAS,只不过不同的厂家的实现不一样罢了。CAS有三个操作数:内存值V、旧的预期值A、要修改的值B,当且仅当预期值A和内存值V相同时,将内存值修改为B并返回true,否则什么都不做并返回false。
先看一段代码
public int a = 1;
public boolean compareAndSwapInt(int b) {
if (a == 1) {
a = b;
return true;
}
return false;
}
试想这段代码在多线程并发下,会发生什么?我们不妨来分析一下:
CAS中的比较和替换是一组原子操作,不会被外部打断,先根据paramLong/paramLong1获取到内存当中当前的内存值V,在将内存值V和原值A作比较,要是相等就修改为要修改的值B,属于硬件级别的操作,效率比加锁操作高。
java.util.concurrent.atomic包下的原子操作类都是基于CAS实现的,接下去我们通过AtomicInteger来看看是如何通过CAS实现原子操作的:
public class AtomicInteger extends Number implements java.io.Serializable {
// setup to use Unsafe.compareAndSwapInt for updates
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); }
}
private volatile int value;
public final int get() {return value;}
}
接下去,我们看看AtomicInteger是如何实现并发下的累加操作:
//jdk1.8实现
public final int getAndAdd(int delta) {
return unsafe.getAndAddInt(this, valueOffset, delta);
}
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;
}
在jdk1.8中,比较和替换操作放在unsafe类中实现。
假设现在线程A和线程B同时执行getAndAdd操作:
整个过程中,利用CAS保证了对于value的修改的线程安全性。
我们继续深入看看Unsafe类中的compareAndSwapInt方法。
public final native boolean compareAndSwapInt(Object paramObject, long paramLong, int paramInt1, int paramInt2);
可以看到,这是一个本地方法调用,这个本地方法在openjdk中依次调用c++代码:unsafe.cpp,atomic.cpp,atomic_window_x86.inline.hpp。下面是对应于intel X86处理器的源代码片段。
inline jint Atomic::cmpxchg (jint exchange_value, volatile jint* dest, jint compare_value) {
int mp = os::isMP(); //判断是否是多处理器
_asm {
mov edx, dest
mov ecx, exchange_value
mov eax, compare_value
LOCK_IF_MP(mp)
cmpxchg dword ptr [edx], ecx
}
}
从上面的源码中可以看出,会根据当前处理器类型来决定是否为cmpxchg指令添加lock前缀。
intel手册对lock前缀的说明如下:
上面的第2点和第3点所具有的内存屏障效果,保证了CAS同时具有volatile读和volatile写的内存语义。
CAS存在一个很明显的问题,即ABA问题。
如果变量V初次读取的时候是A,并且在准备赋值的时候检查到它仍然是A,那能说明它的值没有被其他线程修改过了吗?如果在这段期间它的值曾经被改成了B,然后又改回A,那CAS操作就会误认为它从来没有被修改过。针对这种情况,java并发包中提供了一个带有标记的原子引用类"AtomicStampedReference",它可以通过控制变量值的版本来保证CAS的正确性。
参考:
http://www.jianshu.com/p/fb6e91b013cc
标签:cat imp fse val 指令 uri 获取 判断 cep
原文地址:http://www.cnblogs.com/winner-0715/p/7142343.html