Java并发编程之CAS和Unsafe类本地使用方法-创新互联
- Java 并发编程之CAS 和 Unsafe类本地使用方法
- CAS原理与Unsafe类
- Unsafe类本地使用方法
我们知道保证线程安全的三个要素是原子性,可见性,有序性
CAS(Compare And Swap),指令级别保证某一内存地址V上的值的更新修改是一个原子操作
需要三个值:
内存地址V
该线程拿到的值A
期望更新后的值B
思路:如果地址V上的实际值和该线程拿到的值A相等,就给地址V赋给新值B,如果不是,不做任何操作。
循环(死循环,自旋)里不断的进行CAS操作
JDK里为我们提供了这些原子操作类
- 更新基本类型类:
AtomicBoolean
,AtomicInteger
,AtomicLong
,AtomicReference
- 更新数组类:
AtomicIntegerArray
,AtomicLongArray
,AtomicReferenceArray
- 更新引用类型:
AtomicReference
,AtomicMarkableReference
,AtomicStampedReference
- 原子更新字段类:
AtomicReferenceFieldUpdater
,AtomicIntegerFieldUpdater
,AtomicLongFieldUpdater
观察这些类的源码我们可以发现,CAS底层的原理实现都需要借助一个Unsafe
类来实现,比如对于AtomicInteger
类的compareAndSet
方法:
public final boolean compareAndSet(int expect, int update) {return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
}
代码中unsafe变量的初始化:
private static final Unsafe unsafe = Unsafe.getUnsafe();
于是,为了尝试使用CAS在本地操作,模仿了上面的代码和初始化,尝试在本地进行测试,代码如下:
public class CASTest {static volatile long valueOffset;
// Unsafe类初始化
static Unsafe unsafe = sun.misc.Unsafe.getUnsafe();
static {// initUnsafe();
try {valueOffset = unsafe.objectFieldOffset
(CASTest.class.getDeclaredField("value"));
} catch (Exception ex) {throw new Error(ex); }
}
private static void initUnsafe() {try {Field f = Unsafe.class.getDeclaredField("theUnsafe");
f.setAccessible(true);
Unsafe unsafe1 = (Unsafe) f.get(null);
unsafe = unsafe1;
} catch (IllegalAccessException e) {e.printStackTrace();
} catch (NoSuchFieldException e) {e.printStackTrace();
}
}
volatile int value;
public CASTest(int value) {this.value = value;
}
// 测试cas操作
public void cas() {System.out.println(unsafe.compareAndSwapInt(this, valueOffset , 5, 10));
System.out.println("this.value:" + this.value);
System.out.println(unsafe.compareAndSwapInt(this, valueOffset , 5, 20));
System.out.println("this.value:" + this.value);
}
public static void main(String[] args) {new CASTest(5).cas();
}
}
但是执行后发现,会报错:
Exception in thread "main" java.lang.ExceptionInInitializerError
Caused by: java.lang.SecurityException: Unsafe
at sun.misc.Unsafe.getUnsafe(Unsafe.java:90)
at com.lagou.concurrent.demo.test.CASTest.(CASTest.java:9)
查询相关资料后发现,在使用该getUnsafe方法是,会判断classLoader的类型,如果不是systemClassLoader则会抛出SecurityException(“Unsafe”)异常,所以用户编写的程序使用不了unsafe实例。
那如果我们想本地实现可以怎么办呢?
Unsafe类本地使用方法下面给出一个本地使用Unsafe类初始化的方法,也是网上使用比较多的方法
private static void initUnsafe() {try {Field f = Unsafe.class.getDeclaredField("theUnsafe");
f.setAccessible(true);
Unsafe unsafe1 = (Unsafe) f.get(null);
unsafe = unsafe1;
} catch (IllegalAccessException e) {e.printStackTrace();
} catch (NoSuchFieldException e) {e.printStackTrace();
}
}
使用该初始化方法更新上面的测试代码:
public class CASTest {static volatile long valueOffset;
static Unsafe unsafe;
static {initUnsafe();
try {valueOffset = unsafe.objectFieldOffset
(CASTest.class.getDeclaredField("value"));
} catch (Exception ex) {throw new Error(ex); }
}
private static void initUnsafe() {try {Field f = Unsafe.class.getDeclaredField("theUnsafe");
f.setAccessible(true);
Unsafe unsafe1 = (Unsafe) f.get(null);
unsafe = unsafe1;
} catch (IllegalAccessException e) {e.printStackTrace();
} catch (NoSuchFieldException e) {e.printStackTrace();
}
}
volatile int value;
public CASTest(int value) {this.value = value;
}
public void cas() {System.out.println(unsafe.compareAndSwapInt(this, valueOffset , 5, 10));
System.out.println("this.value:" + this.value);
System.out.println(unsafe.compareAndSwapInt(this, valueOffset , 5, 20));
System.out.println("this.value:" + this.value);
}
public static void main(String[] args) {new CASTest(5).cas();
}
}
正常执行,且输出结果为:
true
this.value:10
false
this.value:10
对于输出结果,根据CAS原理我们分析可知,初始时,我们赋值value=5,那么第一个CAS操作时valueOffset地址对应的value值=5,与compareAndSwapInt
参数里的期望值5匹配,因此CAS操作成功返回true,同时value值被赋为10。同理,第二次CAS操作取valueOffset地址对应的value=10,与方法中的期望值5不匹配,则CAS操作失败返回false,此时value的值仍为10。
如果我们把第二次CAS操作的期望值设成10,那么最终的返回value值会为20。
true
this.value:10
true
this.value:20
你是否还在寻找稳定的海外服务器提供商?创新互联www.cdcxhl.cn海外机房具备T级流量清洗系统配攻击溯源,准确流量调度确保服务器高可用性,企业级服务器适合批量采购,新人活动首月15元起,快前往官网查看详情吧
当前文章:Java并发编程之CAS和Unsafe类本地使用方法-创新互联
文章出自:http://hbruida.cn/article/gihhg.html