CAS(Compare And Swap)

CAS可以简单的理解为比较并替换,利用CPU的CAS指令,同时借助JNI(Java Native Interface为JAVA本地调用)来完成Java的非阻塞算法,实现原子操作。
CAS机制中,使用了3个参数,当前状态下内存中的数值:a;内存中原值:b(该值不是当前状态下内存中的值,而实在操作开始时,从内存中读取的值);要修改的新值:c。在更新一个变量的时候,当且仅当a与b的值相等的时候,才会将内存中的值(也就时当前内存中的值a)更新为c。如果a与b的值不相等,则重新读取内存中的值,进行相应的数据操作之后,再重复这样的过程来对值进行更新。即:通过自旋来进行更新操作。

CAS的缺点以及相应的优化

  1. ABA问题。CAS通过判断原值与当前值是否一致,即,内存中的值没有过变化,从而进行更新操作。如果有内存中的初始值为A,另外一个线程将值修改为B,再修改为A,再做比较时,并未发现内存值的变化,但是,实际上值是有变化的。在JDK1.5之后提供了AtomicStampedReference类来解决ABA问题,保存元素的引用,引用相当于版本号,是每一个变量的标识,因此在CAS前判断下是否是同一个引用即可
  2. 自旋时间长,CPU开销大。
  3. 只能保证一个共享变量的原子操作,当有多个共享变量需要原子操作时,CAS就无法保证了。可以将多个变量合并成一个共享变量(JAVA的组合),JDK1.5以后提供了AtomicReference类,来保证引用对象之间的原子性,可以将多个变量放到一个对象中,进行原子操作。

AQS(AbustactQueuedSynchronizer)

AbustactQueuedSynchronizer维护了一个volatile int类型的变量state,用于表示当前同步状态(volatile虽然不能保证操作的原子性,但是保证了当前变量state的可见性,即:值的改变会立刻同步到主内存中,对于其余线程可见)和一个FIFO线程等待队列(多线程争用资源被阻塞时会进入此队列)。
源码中,对于state的操作如下:

/**
     * The synchronization state.
     */
    private volatile int state;
  
    /**
     * Returns the current value of synchronization state.
     * This operation has memory semantics of a {@code volatile} read.
     * @return current state value
     */
    protected final int getState() {
        return state;
    }

    /**
     * Sets the value of synchronization state.
     * This operation has memory semantics of a {@code volatile} write.
     * @param newState the new state value
     */
    protected final void setState(int newState) {
        state = newState;
    }

    /**
     * Atomically sets synchronization state to the given updated
     * value if the current state value equals the expected value.
     * This operation has memory semantics of a {@code volatile} read
     * and write.
     *
     * @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.
     */
    protected final boolean compareAndSetState(int expect, int update) {
        // See below for intrinsics setup to support this
        return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
    }

其中 unsafe是 java通过调用本地方法,提供了硬件级别的原子操作。

AQS定义两种资源共享方式:Exclusive(独占式),如:ReentrantLock;Share(共享式),如:CountDownLatch。具体的同步类,只需要实现共享资源state的获取与释放方式即可,至于具体线程等待队列的维护(如获取资源失败入队/唤醒出队等),AQS已经实现好了。具体的同步类只需要实现以下几个方法即可:

  • isHeldExclusively():该线程是否正在独占资源。只有用到condition才需要去实现它。
  • tryAcquire(int):独占方式。尝试获取资源,成功则返回true,失败则返回false。
  • tryRelease(int):独占方式。尝试释放资源,成功则返回true,失败则返回false。
  • tryAcquireShared(int):共享方式。尝试获取资源。负数表示失败;0表示成功,但没有剩余可用资源;正数表示成功,且有剩余资源。
  • tryReleaseShared(int):共享方式。尝试释放资源,如果释放后允许唤醒后续等待结点返回true,否则返回false。

CAS和AQS为java并发提供了坚实的基础,后面结合具体的同步类ReentrantLock和CountDownLatch的源码,再来更好的理解这两个原理