ReentrantLock中的CAS与volatile的state

Spring Wu 641 2021-02-03

先说结果,CAS与volatile都拥有内存可见性和内存屏障,也就是说cas具备与volatile一样的volatile读与volatile写。

volatile

ReenteantLock 使用volatile来修饰了state变量,volatile具有这些特点:保证内存可见性,以及防止指令重排序。

如何保证内存可见性

底层通过使用lock指令来实现volatile。其中lock指令在多核处理器下会有以下操作:

  • 将当前处理器中缓存行中的数据立即写回主存。
  • 数据写回主存后,会导致其他线程所持有的旧数据的内存地址数据无效。

从而保证了内存可见性。

如何防止指令重排序

volatile底层使用内存屏障来防止指令重排序,具体的内存屏障有以下几种:

LoadLoad屏障:

抽象场景:Load1; LoadLoad; Load2

Load1 和 Load2 代表两条读取指令。在Load2要读取的数据被访问前,保证Load1要读取的数据被读取完毕。

StoreStore屏障:

抽象场景:Store1; StoreStore; Store2

Store1 和 Store2代表两条写入指令。在Store2写入执行前,保证Store1的写入操作对其它处理器可见

LoadStore屏障:

抽象场景:Load1; LoadStore; Store2

在Store2被写入前,保证Load1要读取的数据被读取完毕。

StoreLoad屏障:

抽象场景:Store1; StoreLoad; Load2

在Load2读取操作执行前,保证Store1的写入对所有处理器可见。StoreLoad屏障的开销是四种屏障中最大的。

Unsafe包中的CAS(compareAndSwap)

Unsafe包中的方法都为native方法,所以。cas也是通过第三方语言(C++)实现。实现cas时,也用到了lock指令。这也就是为什么cas也具有volatile读和volatile写的内存语义了。

ReentrantLock中,分为fairLock公平锁与nonfairLock

释放锁

其中释放公平锁和非公平锁时,都会cas去写state变量。

为什么要去写state呢,state变量在ReentrantLock是一个重要的存在,通过state可以知道锁是否是被持有。如果state > 0,表示当前锁有线程持有。如果state == 0说明当前锁没有被任何线程持有。

所以,当线程使用完锁以后,必须把state的状态改写为0。这样才代表当前锁可用。

获取锁

公平锁的源码:

    static final class FairSync extends Sync {
        private static final long serialVersionUID = -3000897897090466540L;

        final void lock() {
            acquire(1);
        }

        /**
         * Fair version of tryAcquire.  Don't grant access unless
         * recursive call or no waiters or is first.
         */
        protected final boolean tryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            int c = getState();
            if (c == 0) {
                if (!hasQueuedPredecessors() &&
                    compareAndSetState(0, acquires)) {
                    setExclusiveOwnerThread(current);
                    return true;
                }
            }
            else if (current == getExclusiveOwnerThread()) {
                int nextc = c + acquires;
                if (nextc < 0)
                    throw new Error("Maximum lock count exceeded");
                setState(nextc);
                return true;
            }
            return false;
        }
    }

公平锁相对于非公平锁多了一个!hasQueuedPredecessors()方法的调用。

public final boolean hasQueuedPredecessors() {
        // The correctness of this depends on head being initialized
        // before tail and on head.next being accurate if the current
        // thread is first in queue.
        Node t = tail; // Read fields in reverse initialization order
        Node h = head;
        Node s;
        return h != t &&
            ((s = h.next) == null || s.thread != Thread.currentThread());
    }

该方法主要是判断当前线程是否处于队列中第一个。如果是则返回true,否则返回false。// TODO