welcome to my blog
AQS没有使用synchronized锁,而是通过volatile和CAS(Compare And Swap)解决资源的线程安全问题;
线程安全的三个要素:有序性, 可见性, 原子性
volatile能够保证有序性和可见性
CAS能够保证单个变量的原子性
那么CAS是一种无锁操作, 那么它是如何保证变量的原子性呢?
以AtomicInteger类中的getAndIncrement()方法为例, 该方法能够实现线程安全的i++操作, 不需要加锁, 而是用的CAS的思想.
代码如下, 可以看到getAndIncrement()方法是通过Unsafe类的getAndAddInt()方法实现的
代码中用到的Unsafe类可以让java操作内存地址, 比如通过对象和偏移量直接从内存中获取对应变量的值; 还要注意的是Unsafe中的方法是原子操作
//AtomicInteger类
//变量value; 相当于i++中的i
private volatile int value;
//创建Unsafe类的实例
private static final Unsafe unsafe = Unsafe.getUnsafe();
//变量value的偏移量, 具体赋值是在下面的静态代码块中中进行的
private static final long valueOffset;
//在静态代码块中获取变量value的偏移量
static {
try {
//获取value变量的偏移量, 赋值给valueOffset
valueOffset = unsafe.objectFieldOffset(AtomicInteger.class.getDeclaredField("value"));
} catch (Exception ex) { throw new Error(ex); }
}
//执行value++操作
public final int getAndIncrement() {
//this是当前对象, valueOffset是
return unsafe.getAndAddInt(this, valueOffset, 1);
}
CAS的具体实现在Unsafe类的getAndAddInt()方法有体现, 代码如下
//Unsafe类
//获取内存地址为obj+offset的变量值, 并将该变量值加上delta
public final int getAndAddInt(Object obj, long offset, int delta) {
int v;
do {
//通过对象和偏移量获取变量的值
//由于volatile的修饰, 所有线程看到的v都是一样的
v= this.getIntVolatile(obj, offset);
/*
while中的compareAndSwapInt()方法尝试修改v的值,具体地, 该方法也会通过obj和offset获取变量的值
如果这个值和v不一样, 说明其他线程修改了obj+offset地址处的值, 此时compareAndSwapInt()返回false, 继续循环
如果这个值和v一样, 说明没有其他线程修改obj+offset地址处的值, 此时可以将obj+offset地址处的值改为v+delta, compareAndSwapInt()返回true, 退出循环
Unsafe类中的compareAndSwapInt()方法是原子操作, 所以compareAndSwapInt()修改obj+offset地址处的值的时候不会被其他线程中断
*/
} while(!this.compareAndSwapInt(obj, offset, v, v + delta));
return v;
}