ReentrantReadWriterLock原始碼(state設計、讀寫鎖、共享鎖、獨佔鎖及鎖降級)

曹自標發表於2020-12-18

ReentrantReadWriterLock

讀寫鎖類圖(截圖來源https://blog.csdn.net/wangbo199308/article/details/108688148)
在這裡插入圖片描述

state的設計

讀寫鎖將變數state切分成兩個部分,高16位表示讀,低16位表示寫

在這裡插入圖片描述

原始碼中將4位元組(32位)的int資料型別state,通過SHARED_SHIFT(16)劃分讀和寫;

每次讀鎖增加的單元,SHARED_UNIT = (1 << SHARED_SHIFT) 也即0x00010000,即每次讀鎖增加從17位開始加1

讀寫鎖最大數量:MAX_COUNT = (1 << SHARED_SHIFT) - 1,16位最大值

寫鎖的掩碼:EXCLUSIVE_MASK = (1 << SHARED_SHIFT) - 1, 即求寫鎖數量,將state和此掩碼做與運算,將高16位抹去

計算讀鎖數量邏輯:c >>> SHARED_SHIFT,取高16位

計算寫鎖數量邏輯:c & EXCLUSIVE_MASK,將state和此掩碼做與運算,將高16位抹去

public class ReentrantReadWriteLock
        implements ReadWriteLock, java.io.Serializable {
    abstract static class Sync extends AbstractQueuedSynchronizer {
        //16位劃分讀和寫
        static final int SHARED_SHIFT   = 16;
        static final int SHARED_UNIT    = (1 << SHARED_SHIFT);
        static final int MAX_COUNT      = (1 << SHARED_SHIFT) - 1;
        static final int EXCLUSIVE_MASK = (1 << SHARED_SHIFT) - 1;
        
        static int sharedCount(int c)    { return c >>> SHARED_SHIFT; }
        static int exclusiveCount(int c) { return c & EXCLUSIVE_MASK; }
    }
}
讀鎖

讀鎖上鎖的呼叫鏈:ReentrantReadWriteLock$ReadLock#lock() -->AbstractQueuedSynchronizer#acquireShared() -->ReentrantReadWriteLock$Sync#tryAcquireShared()

當前寫鎖數量為0或獨佔鎖持有者就是當前執行緒才進行讀鎖邏輯

讀鎖數量通過CAS加1

之後邏輯是將讀鎖執行緒放入ThreadLocal中,記錄各自鎖數量

public class ReentrantReadWriteLock
        implements ReadWriteLock, java.io.Serializable {
    public static class ReadLock implements Lock, java.io.Serializable {
        public void lock() {
            sync.acquireShared(1);
        }
    }
}
public abstract class AbstractQueuedSynchronizer
    extends AbstractOwnableSynchronizer
    implements java.io.Serializable {
    public final void acquireShared(int arg) {
        if (tryAcquireShared(arg) < 0)
            doAcquireShared(arg);
    }
}
public class ReentrantReadWriteLock
        implements ReadWriteLock, java.io.Serializable {
    abstract static class Sync extends AbstractQueuedSynchronizer {
        protected final int tryAcquireShared(int unused) {
            Thread current = Thread.currentThread();
            int c = getState();
            // 同時滿足寫鎖數量不為0,且獨佔鎖不是當前執行緒,走doAcquireShared邏輯
            if (exclusiveCount(c) != 0 &&
                getExclusiveOwnerThread() != current)
                return -1;
            // 取高16位讀鎖數量
            int r = sharedCount(c);
            if (!readerShouldBlock() &&
                r < MAX_COUNT &&
                compareAndSetState(c, c + SHARED_UNIT)) {
                // ThreadLocal存放鎖資訊
                if (r == 0) {
                    firstReader = current;
                    firstReaderHoldCount = 1;
                } else if (firstReader == current) {
                    firstReaderHoldCount++;
                } else {
                    HoldCounter rh = cachedHoldCounter;
                    if (rh == null || rh.tid != getThreadId(current))
                        cachedHoldCounter = rh = readHolds.get();
                    else if (rh.count == 0)
                        readHolds.set(rh);
                    rh.count++;
                }
                return 1;
            }
            return fullTryAcquireShared(current);
        }
    }
}

在讀鎖獲取鎖過程,寫鎖不為0且佔有寫鎖的不是當前執行緒,返回-1,走同步器doAcquireShared方法,等待寫鎖釋放;

前置節點是head節點時,嘗試獲取共享鎖

private void doAcquireShared(int arg) {
    // 佇列加入的node是共享模式
    final Node node = addWaiter(Node.SHARED);
    boolean failed = true;
    try {
        boolean interrupted = false;
        for (;;) {
            final Node p = node.predecessor();
            if (p == head) {
                //前置節點是head節點時,嘗試獲取共享鎖
                int r = tryAcquireShared(arg);
                if (r >= 0) {
                    setHeadAndPropagate(node, r);
                    p.next = null; // help GC
                    if (interrupted)
                        selfInterrupt();
                    failed = false;
                    return;
                }
            }
            if (shouldParkAfterFailedAcquire(p, node) &&
                parkAndCheckInterrupt())
                interrupted = true;
        }
    } finally {
        if (failed)
            cancelAcquire(node);
    }
}
寫鎖
  1. 讀鎖不為0,但寫鎖為0,獲取鎖失敗;讀鎖不為0,寫鎖也不為0,但獨佔鎖不是當前執行緒,獲取鎖失敗
  2. 如果鎖數量已到最大,獲取失敗
  3. 否則獲取寫鎖,更新state
public class ReentrantReadWriteLock
        implements ReadWriteLock, java.io.Serializable {
    abstract static class Sync extends AbstractQueuedSynchronizer {
        protected final boolean tryAcquire(int acquires) {

            Thread current = Thread.currentThread();
            int c = getState();
            int w = exclusiveCount(c);
            if (c != 0) {
                // (Note: if c != 0 and w == 0 then shared count != 0)
                if (w == 0 || current != getExclusiveOwnerThread())
                    return false;
                if (w + exclusiveCount(acquires) > MAX_COUNT)
                    throw new Error("Maximum lock count exceeded");
                // Reentrant acquire
                setState(c + acquires);
                return true;
            }
            if (writerShouldBlock() ||
                !compareAndSetState(c, c + acquires))
                return false;
            setExclusiveOwnerThread(current);
            return true;
        }
    }
}
共享鎖和獨佔鎖

讀鎖是共享鎖,當執行緒1獲得讀鎖時,並不會排斥執行緒2去獲取讀鎖,而是在ThreadLocal中儲存每個鎖數量

    abstract static class Sync extends AbstractQueuedSynchronizer {
        static final class HoldCounter {
            int count = 0;
            // Use id, not reference, to avoid garbage retention
            final long tid = getThreadId(Thread.currentThread());
        }
        
        static final class ThreadLocalHoldCounter
            extends ThreadLocal<HoldCounter> {
            public HoldCounter initialValue() {
                return new HoldCounter();
            }
        }
    }

寫鎖是獨佔鎖,會呼叫同步器AbstractQueuedSynchronizer#acquire()方法,預設加入佇列的node模式是獨佔模式

    public final void acquire(int arg) {
        if (!tryAcquire(arg) &&
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
            selfInterrupt();
    }
鎖降級

鎖降級就是從寫鎖降級成為讀鎖。在當前執行緒擁有寫鎖的情況下,再次獲取到讀鎖,隨後釋放寫鎖的過程就是鎖降級

鎖降級示例:

public void processData() {
    ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
    ReentrantReadWriteLock.ReadLock readLock = lock.readLock();
    ReentrantReadWriteLock.WriteLock writeLock = lock.writeLock();
    readLock.lock();
    if(!update) {
        //必須先釋放讀鎖
        readLock.unlock();
        // 鎖降級從寫鎖獲取到開始
        writeLock.lock();
        try{
            if(!update) {
                update = true;
            }
            // 可以獲取到讀鎖,getExclusiveOwnerThread() == current
            readLock.lock();
        } finally {
            writeLock.unlock();
        }
        //鎖降級完成,寫鎖降級為讀鎖
    }
    try{
        // 使用資料的流程
    } finally {
        readLock.unlock();
    }
}

可降級的原始碼仍是在讀鎖tryAcquireShared方法中,getExclusiveOwnerThread() == current,也即當前獨佔鎖owner就是當前執行緒,可進行讀鎖邏輯。

protected final int tryAcquireShared(int unused) {
    if (exclusiveCount(c) != 0 &&
        getExclusiveOwnerThread() != current)
        return -1;
}

參考:《Java併發程式設計的藝術》

相關文章