ReentrantLock源码系列
ReentrantLock源码解读(1)——CAS
ReentrantLock源码解读(2)——ReentrantLock源码与AQS
ReentrantLock源码解读(3)——Condition
进入源码前先介绍一个“无锁”技术:CAS(compare and swap)。为什么要介绍它呢,因为ReentrantLock的锁就是利用它实现的。
它是一种乐光的策略,就是乐观地认为访问资源是不会冲突的,也就是所有的线程都可以不等待地进行下去。遇到冲突怎么办?检测到冲突就是一直重试,直到没有冲突为止。
简单说,CAS需要你额外给定一个期望值,也就是你认为这个状态是什么样子,如果不是,就说明被人改过了,那就重新读取、设定期望值,再次尝试修改。
- 例子
public class CASTest {
static AtomicInteger atomicInteger = new AtomicInteger();
static class Increment implements Runnable {
@Override
public void run() {
for (int i = 0; i < 1000; i++) {
atomicInteger.incrementAndGet();
}
}
}
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(new Increment());
Thread t2 = new Thread(new Increment());
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println(atomicInteger); // 输出2000
}
}
这里用到了一个AtomicInteger类,它是jdk包装的无锁的线程安全的整数类。incrementAndGet()是它的自增方法,上述例子输出2000说明这个方法是线程安全的,它底层用的就是CAS操作,具体这样的,来看看源码。
- 源码
// 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;
// ...省略部分代码
/**
* Atomically increments by one the current value.
*
* @return the updated value
*/
public final int incrementAndGet() {
return unsafe.getAndAddInt(this, valueOffset, 1) + 1;
}
首先这个类里面有个volatile修饰的int,对这个类的它就是这个类的实际取值。
还有一个特殊的类Unsafe,这个类是CAS的核心类。既然命名成Unsafe,说明它不安全,所以它是JDK内部使用的专属类。
还有一个valueOffset,也是通过Unsafe类拿到的。这个东西是对象内“value”字段的偏移量,通过这个偏移量,我们可以快速定位到value字段。
再往下看看Unsafe类getAndAddInt()方法的实现。
public final native boolean compareAndSwapInt(Object var1, long var2, int var4, int var5);
public final int getAndAddInt(Object var1, long var2, int var4) {
int var5;
do {
var5 = this.getIntVolatile(var1, var2);
} while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));
return var5;
}
摘取了部分带码。getAndAddInt()方法里,不断地循环获取最新值然后调compareAndSwapInt()方法去set值,直到成功。
compareAndSwapInt()方法是个native方法,它是C++实现的,它是个原子性方法。
再介绍一下compareAndSwapInt()几个方法的参数:var1为给定的对象,var2我偏移量上面有介绍,var3为期望值,var4为新值。
当然,除了compareAndSwapInt(),Unsafe还有类似其他方法。
public final native boolean compareAndSwapObject(Object var1, long var2, Object var4, Object var5);
public final native boolean compareAndSwapLong(Object var1, long var2, long var4, long var6);
- 缺点
- 循环操作,如果多线程互相争抢一直不能set1成功,销毁过多资源。
解决:设置一定的次数或者超时时间,达到后return,或者阻塞一段时间。 - ABA问题,一个线程把状态值从A改为B,再改为A,另一个线程去set的时候发现还是A,并不知道被改过了。
解决:加上一个时间戳或者版本号。例如AtomicStampedReference就是加上了一个时间戳 - CAS只能操作一个变量,无法操作多个变量。
解决:封装成一个对象