前言
在JDK5新增的并发包中有这样一个包:java.util.concurrent.atomic,该包是对Java部分数据类型的原子封装,在原有数据类型的基础上,提供了原子性的操作方法,保证了线程安全,本文我们以AtomicInteger为例,来看看原子类型是如何通过CAS实现原子性操作的。
Compare And Swap
CAS 指的是现代 CPU 广泛支持的一种对内存中的共享数据进行操作的一种特殊指令。这个指令会对内存中的共享数据做原子的读写操作。其操作过程如下:
- CPU 会将内存中将要被更改的数据与期望的值做比较。当这两个值相等时,CPU 才会将内存中的数值替换为新的值。否则便不做操作。
- CPU 会将旧的数值返回。
简单来说,CAS 的含义是“我认为原有的值应该是什么,如果是,则将原有的值更新为新值,否则不做修改,并告诉我原来的值是多少”。
这一系列的操作是原子的。它们虽然看似复杂,但却是 Java 5 并发机制优于原有锁机制的根本。这是一种乐观锁的思路,它相信在它修改之前,没有其它线程去修改它;而Synchronized是一种悲观锁,它认为在它修改之前,一定会有其它线程去修改它,悲观锁效率很低。
AtomicInteger
下面我们以AtomicInteger为例,来看看原子类型是如何通过CAS实现原子性操作的。
volatile变量
private volatile int value;
public final int get() {
return value;
}
首先声明了一个volatile变量value,volatile关键字保证了变量的内存可见性,保证共享变量被线程加载到工作内存的值是最新的;
内存可见性:一个线程对共享变量的写操作对后续其他线程的读操作可见。
Compare And Set
// 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"));// 反射出value属性,获取其在内存中的位置
} catch (Exception ex) { throw new Error(ex); }
}
public final boolean compareAndSet(int expect, int update) {
return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
}
这里利用Unsafe类的JNI方法实现,使用CAS指令,可以保证读-改-写是一个原子操作。compareAndSwapInt有4个参数,this - 当前AtomicInteger对象,valueOffset- value属性在内存中的位置(需要强调的是不是value值在内存中的位置),expect - 预期值,update - 新值。根据上面介绍的CAS操作过程,当内存中的value值等于expect值时,则将内存中的value值更新为update值,并返回true,否则返回false。
原子性方法
AtomicInteger中提供了许多原子性方法,比如public final int getAndIncrement (),public final int getAndSet (int newValue),public final int incrementAndGet ()等,下面看看incrementAndGet的源码,该方法的作用是在原有值的基础上+1,返回+1后的值:
public final int incrementAndGet() {
for (;;) {// 这样优于while(true)
int current = get();// 获取当前值
int next = current + 1;// 设置更新值
if (compareAndSet(current, next))
return next;
}
}
从源码中可以看到使用了compareAndSet方法。