# CAS 乐观锁 🔨

synchronized 实际上是一种悲观锁,这种线程一旦得到锁,其他需要锁的线程就挂起的情况就是悲观锁CAS操作的就是乐观锁,每次不加锁而是假设没有冲突而去完成某项操作,如果因为冲突失败就重试,直到成功为止。

CAS是英文单词Compare And Swap的缩写,翻译过来就是比较并替换。

CAS机制当中使用了3个基本操作数:内存地址V旧的预期值A要修改的新值B

更新一个变量的时候,只有当变量的预期值A和内存地址V当中的实际值相同时,才会将内存地址V对应的值修改为B。

原子操作类,指的是java.util.concurrent.atomic包下,一系列以Atomic开头的包装类。例如AtomicBooleanAtomicIntegerAtomicLong。它们分别用于BooleanIntegerLong类型的原子性操作,而Atomic操作的底层实现正是利用的CAS机制

CAS机制 也是有缺点的,如下几点:

  1. CPU开销较大: 在并发量比较高的情况下,如果许多线程反复尝试更新某一个变量,却又一直更新不成功,循环往复,会给CPU带来很大的压力。
  2. 不能保证代码块的原子性: CAS机制所保证的只是一个变量的原子性操作,而不能保证整个代码块的原子性。比如需要保证3个变量共同进行原子性的更新,就不得不使用synchronized了。
  3. ABA问题: 就是说从A变成B,然后就变成A,但是并不能说明其他线程并没改变过它,利用CAS就发现不了这种改变。

# ABA 的解决办法

  • 在变量前面追加版本号:每次变量更新就把版本号+1
  • atomic包下的AtomicStampedReferenceAtomicMarkableReferenceJDK中解决CASABA问题的两种解决方案,他们的原理是相同的,就是添加一个标记来记录更改,两者的区别如下:
    • AtomicStampedReference : 利用一个int类型的标记来记录,它能够记录改变的次数。
    • AtomicMarkableReference: 利用一个boolean类型的标记来记录,只能记录它改变过,不能记录改变的次数。

# Unsafe

查看 AtomicLong 实现CAS的源码,它是通过调用sun.misc.Unsafe完成 CAS操作

    // java.util.concurrent.atomic.AtomicLong.class
public class AtomicLong extends Number implements java.io.Serializable {
   
   // ...

   /**
     * 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 {@code true} if successful. False return indicates that
     * the actual value was not equal to the expected value.
     */
    public final boolean compareAndSet(long expect, long update) {
        return unsafe.compareAndSwapLong(this, valueOffset, expect, update);
    }
    
    // ...
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

Unsafesun.misc 包下的一个类,可以直接操作堆外内存,可以随意查看及修改JVM中运行时的数据,使Java语言拥有了类似C语言指针一样操作内存空间的能力。

Unsafe的操作粒度不是类,而是内存地址和所对应的数据,增强了Java语言操作底层资源的能力。

Last Updated: a year ago