1.CAS简介(复制的,可以跳过看后面代码实例)
CAS,Compare and Swap即比较并替换,设计并发算法时常用到的一种技术,Doug lea大神在java同步器中大量使用了CAS技术,鬼斧神工的实现了多线程执行的安全性。
目前的处理器基本都支持CAS,只不过不同的厂家的实现不一样罢了。CAS有三个操作数:内存值V、旧的预期值A、要修改的值B,当且仅当预期值A和内存值V相同时,将内存值修改为B并返回true,否则什么都不做并返回false。
public class CAS {
private static Unsafe unsafe ;
static{
loadUnsafe();
}
private static void loadUnsafe() {
try {
/**Unsafe在rt.jar下,不能直接实例化。必须通过反射*/
Field field = Unsafe.class.getDeclaredField("theUnsafe") ;
field.setAccessible(true);
unsafe = (Unsafe)field.get(null);
} catch (Exception e) {
e.printStackTrace();
}
}
static class Data{
int intParam ;
}
public static void main(String[] args) throws NoSuchFieldException, SecurityException {
// objectFieldOffset : 获取 intParam 字段的内存偏移地址
long intParamOffset = unsafe.objectFieldOffset(Data.class.getDeclaredField("intParam")) ;
System.out.println(intParamOffset);
Data data = new Data();
data.intParam = 2 ;
/***
* compareAndSwapInt 比较和 修改值 ,是原子操作
* 参数1: 要修改的对象
* 参数2:对象字段的偏移地址
* 参数3:预期值(如果等于原始值,则修改成功)
* 参数4:修改后的值
*/
boolean ret = unsafe.compareAndSwapInt(data, intParamOffset, 2, 13);//原值2, 等于预期值2,能改成功
if(ret){
System.out.println("修改intParam成功 , val:"+data.intParam);
}
ret = unsafe.compareAndSwapInt(data, intParamOffset, 2, 12);
if(!ret){
System.out.println("修改失败");
}
}
}
执行结果
12
修改intParam成功 , val:13
修改失败
3.AtomicInteger分析(必须要上面基础)
这里就将了一个getAndSet方法,但是我想就已经足够了。详细解释见 注释。
public class AtomicInteger {
// jdk自己可以使用, 所以不用我们上面说的反射来获取 Unsafe类实例
private static final Unsafe unsafe = Unsafe.getUnsafe();
// AtomicInteger 里 value 值得 内存偏移地址
private static final long valueOffset;
// 真正的 int 值
private volatile int value;
static {
try {
valueOffset = unsafe.objectFieldOffset
(AtomicInteger.class.getDeclaredField("value"));
} catch (Exception ex) { throw new Error(ex); }
}
public AtomicInteger(int initialValue) {
value = initialValue;
}
public final int get() {
return value;
}
/***
* 这段代码子在不加锁的情况下通过CAS 实现线程安全,我们设想下方法的执行:
* 1. 假如 AtomicInteger原始值value为3,既 主存中值为3,根据java内存模型,线程1和线程2各自也拥有一份value的副本,值都是3
*
* 2.线程1运行到 int current = get() 时,获取当前value的值为3 ,此时切换到线程2
*
* 3.线程2开始运行,获取到的值也为3,利用CAS对比内存中的值为3,比较成功,修改了内存 ,此时内存的值变成修改后的4(假如),线程又切换
*
* 4.线程1恢复,利用CAS比较 发现自己的值为3,内存的为4,得出一个结论:此时value正在被另一个线程修改,我不能修改它, CAS操作修改失败
*
* 5.由于value是volatile的 ,所以某个时刻,主内存的值肯定会同步到线程1内的值, 所以代码用循环来执行CAS操作,直到值修改成功
*/
public final int getAndSet(int newValue) {
for (;;) {
int current = get();
if (compareAndSet(current, newValue))
return current;
}
}
// CAS 比较操作 ,看上面例子就能懂
public final boolean compareAndSet(int expect, int update) {
return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
}
}
老生常谈:深圳有爱好音乐的会打鼓(吉他,键盘,贝斯等)的程序员和其它职业可以一起交流加入我们乐队一起嗨。我的QQ:657455400