ReentrantLock源码解读(1)——CAS

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);
  • 缺点
  1. 循环操作,如果多线程互相争抢一直不能set1成功,销毁过多资源。
    解决:设置一定的次数或者超时时间,达到后return,或者阻塞一段时间。
  2. ABA问题,一个线程把状态值从A改为B,再改为A,另一个线程去set的时候发现还是A,并不知道被改过了。
    解决:加上一个时间戳或者版本号。例如AtomicStampedReference就是加上了一个时间戳
  3. CAS只能操作一个变量,无法操作多个变量。
    解决:封装成一个对象
已标记关键词 清除标记
相关推荐
©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页