最近看java并发库的源代码,经常看到Unsafe及对应的CAS方法,现在来探究下CAS
什么是CAS?
CAS意指compare and swap,即比较并交换。它代表在java中一类方法,原子的实现数据交换并且判断数据内容是否符合预期。
通常有三个参数,需要交换的对象A,我们预期对象A的值为B,即将交换的A的值C,在单线程中我们只需要如下代码即可:
Object a;//最初的对象
Object b;//预期a的值
Object c;//最终a的值
if(a==b){
a = c;
}
但在多线程中,上面的代码会出现严重的问题,如果线程1经判断a==b,准备执行a=c时,cpu切换到线程2,且将a改成了d,
当cpu重新切换到线程1,此时a已经不等于b了,也即是执行a=c的前提条件已经不满足;
因此我们需要对上面的操作原子的进行,当然我们可以对程序块加锁实现,这里我们讨论另一种实现方法,即使用Unsafe类CAS方法
Unsafe类?
这是sun.misc包中一个类,这并不是一个正常的类,没有办法使用正常的new Unsafe()来创建对象,其虽然提供了静态的getUnsafe方法,但仍然无法使用,调用时会报java.lang.SecurityException: Unsafe的错误。当然我们可以通过反射去绕过限制,但除非必要在正式项目中最好不要使用,拿来探究jvm确实是很有用的类。
private Unsafe() {
}
@CallerSensitive
public static Unsafe getUnsafe() {
Class var0 = Reflection.getCallerClass();
if(var0.getClassLoader() != null) {
throw new SecurityException("Unsafe");
} else {
return theUnsafe;
}
}
例如:可以直接操作内存,下面三个方法分别申请内存,扩充内存及释放内存。
public native long allocateMemory(long paramLong);
public native long reallocateMemory(long paramLong1, long paramLong2);
public native void freeMemory(long paramLong);
CAS方法:以compareAndSwapInt为例,var1是需要更新的变量,var2是相对object对象的偏移量(一般为var1某个整形属性的偏移量),var4是期望值,var5是整形属性的新值。
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);
查看一些文章,在linux中的实现这样一个函数:*ptr 与old 相比较,如相同,则new写入ptr
cmpxchg(void *ptr, unsigned long old, unsigned long new);
下面看一个demo,在这个例子中,我们创建了一个Unsafe实例,并通过它直接创建了一个UserToken对象,我们发现创建过程中并未进行初始化,之后测试了 compareAndSwapInt方法,当age==10时,设置age=30成功,当age!=10时(此时age=30),设置age=20失败。
import sun.misc.Unsafe;
import java.lang.reflect.Field;
/**
* Created by liushuai on 17/5/2.
*/
public class UserToken {
private String name = "test";
private int age =12;
private UserToken(){
name = "1234";
age = 5;
}
public static void main(String[] args) throws Exception {
//获取Unsafe实例
Field f = Unsafe.class.getDeclaredField("theUnsafe"); //Internal reference
f.setAccessible(true);
Unsafe unsafe = (Unsafe) f.get(null);
//绕过UserToken构造方法及初始化,创建对象
UserToken t = (UserToken)unsafe.allocateInstance(UserToken.class);
//并未初始化,所以 age==0
System.out.println(t.age); //0
t.age=10;
System.out.println(t.age); //10
//获取age属性相对对象在内存中的偏移量offset
Field ageFiled = t.getClass().getDeclaredField("age");
long offset = unsafe.objectFieldOffset(ageFiled);
//修改age属性值
boolean flag = unsafe.compareAndSwapInt(t,offset,10,30);
System.out.println(flag);//true
System.out.println(t.age);//30
flag = unsafe.compareAndSwapInt(t,offset,10,20);
System.out.println(flag);//false
System.out.println(t.age);//30
}
}
AtomicInteger中CAS应用
此类位于java.util.concurrent.atomic包下,提供对整形数字的原子操作。
// 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;
从代码中我们看到,AtomicInteger类持有一个静态final修饰的Unsafe对象,并在静态块中初始化获取了此类中私有变量value的对象偏移量,value即是对象包含的真正的整形数值,由volatile关键字修饰,确保对value的修改对所有线程可见,valueOffset即value值在内存中相对于AtomicInteger实例对象的偏移量。根据上面的例子可知,可以使用unsafe根据偏移量获取和修改value的值,而且操作保证原子性。
/**
* Atomically sets the value to the given updated value
* if the current value {@code ==} the expected value.
*
* @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 unsafe.compareAndSwapInt(this, valueOffset, expect, update);//调用unsafe的cas方法
}
以及自增方法:调用链为
getAndIncrement---->compareAndSet----->unsafe.compareAndSwapInt
/**
* Atomically increments by one the current value.
*
* @return the previous value
*/
public final int getAndIncrement() {
for (;;) {
int current = get();
int next = current + 1;
if (compareAndSet(current, next))
return current;
}
}
AQS中状态的变更
AQS全称AbstractQueuedSynchronizer,位于java.util.concurrent.locks包中一个抽象类,这个类提供许多公共的方法供子类使用,其中存储了一个链式结构的双向链表队列,用来存储被阻塞的线程,并且将这部分线程封装成Node对象,并保存了线程的等待状态waitStatus。
/** waitStatus value to indicate thread has cancelled */
static final int CANCELLED = 1;//代表线程已被取消
/** waitStatus value to indicate successor's thread needs unparking */
static final int SIGNAL = -1; //代表后续线程将被唤醒
/** waitStatus value to indicate thread is waiting on condition */
static final int CONDITION = -2; //代表线程等待某一条件
/**
* waitStatus value to indicate the next acquireShared should
* unconditionally propagate
*/
static final int PROPAGATE = -3;//代表后续结点会传播唤醒的操作,共享模式下起作用
volatile int waitStatus;
private volatile int state;
我们暂不讨论AQS实现原理,只探究下它的状态变换机制,及Node替换。如下面我们看到的,对于waitStatus,state,head,tail,nex等变量,都使用了volatile修饰,以确保其修改对所有线程可见,初始化时获取Unsafe实例,并在static静态块中初始化获取以上变量在内存中相对于对象的偏移量。
private static final Unsafe unsafe = Unsafe.getUnsafe(); //获取Unsafe对象
private static final long stateOffset;
private static final long headOffset;
private static final long tailOffset;
private static final long waitStatusOffset;
private static final long nextOffset;
static { //获取属性偏移量
try {
stateOffset = unsafe.objectFieldOffset
(AbstractQueuedSynchronizer.class.getDeclaredField("state"));
headOffset = unsafe.objectFieldOffset
(AbstractQueuedSynchronizer.class.getDeclaredField("head"));
tailOffset = unsafe.objectFieldOffset
(AbstractQueuedSynchronizer.class.getDeclaredField("tail"));
waitStatusOffset = unsafe.objectFieldOffset
(Node.class.getDeclaredField("waitStatus"));
nextOffset = unsafe.objectFieldOffset
(Node.class.getDeclaredField("next"));
} catch (Exception ex) { throw new Error(ex); }
}
/**
* Atomically sets synchronization state to the given updated
* value if the current state value equals the expected value.
* This operation has memory semantics of a <tt>volatile</tt> read
* and write.
*
* @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.
*/
protected final boolean compareAndSetState(int expect, int update) {
// See below for intrinsics setup to support this
return unsafe.compareAndSwapInt(this, stateOffset, expect, update); //状态变换
}
/**
* CAS head field. Used only by enq.
*/
private final boolean compareAndSetHead(Node update) {
return unsafe.compareAndSwapObject(this, headOffset, null, update); //头结点设置
}
/**
* CAS tail field. Used only by enq.
*/
private final boolean compareAndSetTail(Node expect, Node update) {
return unsafe.compareAndSwapObject(this, tailOffset, expect, update); //尾节点替换
}
/**
* CAS waitStatus field of a node.
*/
private static final boolean compareAndSetWaitStatus(Node node, //等待状态更新
int expect,
int update) {
return unsafe.compareAndSwapInt(node, waitStatusOffset,
expect, update);
}
/**
* CAS next field of a node.
*/
private static final boolean compareAndSetNext(Node node,
Node expect,
Node update) {
return unsafe.compareAndSwapObject(node, nextOffset, expect, update);
}
因此我们总结下CAS的应用:
1 获取Unsafe的实例
2 初始化变量的偏移量
3 变量由volatile关键字修饰