系列傳送門:java
咱們以前說到,ReentrantLock是獨佔鎖,某一時刻只有一個線程能夠獲取該鎖,而實際上會存在不少讀多寫少的場景,而讀操做自己並不會存在數據競爭問題,若是使用獨佔鎖,可能會致使其中一個讀線程使其餘的讀線程陷入等待,下降性能。編程
針對這種讀多寫少的場景,讀寫鎖應運而生。讀寫鎖容許同一時刻有多個讀線程訪問,但在寫線程訪問時,全部的讀線程和其餘寫線程均被阻塞。咱們先來看看Java中的讀寫鎖頂級接口吧,位於:java.util.concurrent.locks
包下:c#
public interface ReadWriteLock { // 讀鎖 Lock readLock(); // 寫鎖 Lock writeLock(); }
相信你會一會兒就明白,讀寫鎖其實就是維護了一對鎖,一個寫鎖一個讀鎖,經過讀寫分離的策略,容許多個線程同時獲取讀鎖,大大提升併發性。緩存
JavaDoc文檔寫的很是詳細,給咱們舉了一個ReentrantReadWriteLock的使用例子,咱們直接來看看:架構
class CachedData { Object data; volatile boolean cacheValid; // 建立讀寫鎖實例 final ReentrantReadWriteLock rwl = new ReentrantReadWriteLock(); void processCachedData() { // 獲取讀鎖 rwl.readLock().lock(); // 緩存失效的狀況 if (!cacheValid) { // 釋放掉讀鎖,必須!在獲取寫鎖以前給讀鎖釋放了 rwl.readLock().unlock(); // 獲取寫鎖 rwl.writeLock().lock(); try { // 從新檢查狀態,由於在等待寫鎖的過程當中,可能前面有其餘寫線程執行過了 if (!cacheValid) { data = ... cacheValid = true; } // 持有寫鎖的狀況下,獲取讀鎖的,稱爲 「鎖降級」 rwl.readLock().lock(); } finally { // 釋放寫鎖,此時還剩一個讀鎖 rwl.writeLock().unlock(); } } try { use(data); } finally { // 釋放讀鎖 rwl.readLock().unlock(); } } }
稍微總結一下,詳細的在後面的解析部分:併發
ReentrantReadWriteLock讀寫鎖分爲讀鎖和寫鎖,讀鎖是共享鎖,寫鎖是獨佔鎖。app
持有寫鎖的線程能夠繼續獲取讀鎖,稱爲鎖降級。ide
ReentrantReadWriteLock是ReadWriteLock的實現,其實看到這個名兒:可重入的讀寫鎖,咱們就大概能夠猜想一下它的意思了。除了實現了readLock()
和writeLock()
兩個方法以外,還提供了一些重要方法,咱們待會會一一解析。高併發
public class ReentrantReadWriteLock implements ReadWriteLock, java.io.Serializable { private static final long serialVersionUID = -6992448646407690164L; /** 內部維護ReadLock */ private final ReentrantReadWriteLock.ReadLock readerLock; /** 內部維護WriteL */ private final ReentrantReadWriteLock.WriteLock writerLock; /** 讀、寫鎖公用一個AQS的Sync的實例 */ final Sync sync; /** 默認使用非公平模式 */ public ReentrantReadWriteLock() { this(false); } /** 初始化讀鎖和寫鎖實例 */ public ReentrantReadWriteLock(boolean fair) { sync = fair ? new FairSync() : new NonfairSync(); readerLock = new ReadLock(this); writerLock = new WriteLock(this); } public ReentrantReadWriteLock.WriteLock writeLock() { return writerLock; } public ReentrantReadWriteLock.ReadLock readLock() { return readerLock; } /** * AQS的實現 */ abstract static class Sync extends AbstractQueuedSynchronizer { // ... } /** * Sync 非公平版本的實現 */ static final class NonfairSync extends Sync { private static final long serialVersionUID = -8159625535654395037L; final boolean writerShouldBlock() { return false; // writers can always barge } final boolean readerShouldBlock() { return apparentlyFirstQueuedIsExclusive(); } } /** * Sync 公平版本的實現 */ static final class FairSync extends Sync { private static final long serialVersionUID = -2274990926593161451L; final boolean writerShouldBlock() { return hasQueuedPredecessors(); } final boolean readerShouldBlock() { return hasQueuedPredecessors(); } } /** * 能夠經過ReentrantReadWriteLock#readLock方法獲得一個讀鎖實例 */ public static class ReadLock implements Lock, java.io.Serializable { private static final long serialVersionUID = -5992448646407690164L; private final Sync sync; protected ReadLock(ReentrantReadWriteLock lock) { sync = lock.sync; } // 能夠看到讀鎖使用的是共享模式 public void lock() { sync.acquireShared(1); } public void unlock() { sync.releaseShared(1); } //...省略tryLock、lockInterruptibly等方法 } /** * 能夠經過ReentrantReadWriteLock#writeLock方法得到一個寫鎖實例 */ public static class WriteLock implements Lock, java.io.Serializable { private static final long serialVersionUID = -4992448646407690164L; private final Sync sync; protected WriteLock(ReentrantReadWriteLock lock) { sync = lock.sync; } // 能夠看到讀鎖使用的是獨佔模式 public void lock() { sync.acquire(1); } public void unlock() { sync.release(1); } //...省略tryLock、lockInterruptibly等方法 }
咱們大概總結一下:oop
readLock()
和writeLock()
兩個方法得到。咱們在學習AQS的時候說到過,AQS的關鍵就是同步狀態字段state,例如以ReentrantLock爲例,它的state爲0表示鎖空閒,爲1表示有鎖被獲取,大於1表示鎖被同一個線程重入。
但已知讀寫鎖須要維護兩種狀態,僅用一個整型變量state如何表示呢?讀寫鎖利用按位切割的思想,巧妙地將state分割爲兩部分:
注意區別這二者的區別。
/* * Read vs write count extraction constants and functions. * Lock state is logically divided into two unsigned shorts: * The lower one representing the exclusive (writer) lock hold count, * and the upper the shared (reader) hold count. */ static final int SHARED_SHIFT = 16; // 共享鎖狀態單位值 65536 static final int SHARED_UNIT = (1 << SHARED_SHIFT); // 共享鎖線程最大個數 65535 static final int MAX_COUNT = (1 << SHARED_SHIFT) - 1; // 排他鎖掩碼 65535 二進制表示 15個1 static final int EXCLUSIVE_MASK = (1 << SHARED_SHIFT) - 1; /** 返回讀鎖的獲取次數【包括重入次數】 無符號補0右移16位,其實就是獲取高16位 */ static int sharedCount(int c) { return c >>> SHARED_SHIFT; } /** 返回寫鎖可重入次數 將高16位抹去,其實就是獲取低16位 */ static int exclusiveCount(int c) { return c & EXCLUSIVE_MASK; }
// 記錄每一個線程持有的讀鎖數量 static final class HoldCounter { // 持有的讀鎖數 int count = 0; // Use id, not reference, to avoid garbage retention // 線程id final long tid = getThreadId(Thread.currentThread()); } /** * ThreadLocal subclass. Easiest to explicitly define for sake * of deserialization mechanics. * ThreadLocal的子類 */ static final class ThreadLocalHoldCounter extends ThreadLocal<HoldCounter> { // 每一個線程都須要記錄獲取讀鎖的次數,判斷是否重入 public HoldCounter initialValue() { return new HoldCounter(); } } // ThreadLocalHoldCounter繼承ThreadLocal // 存放除去第一個獲取讀鎖線程外的其餘線程獲取讀鎖的可重入次數 private transient ThreadLocalHoldCounter readHolds; // 記錄最後一個獲取讀鎖的線程獲取讀鎖的可重入次數 private transient HoldCounter cachedHoldCounter; // 記錄第一個獲取到讀鎖的線程 private transient Thread firstReader = null; // 記錄第一個獲取到讀鎖的線程獲取讀鎖的可重入次數 private transient int firstReaderHoldCount; Sync() { // 初始化readHolds readHolds = new ThreadLocalHoldCounter(); // 保證readHolds的內存可見性 setState(getState()); // ensures visibility of readHolds }
ReentrantReadWriteLock中的寫鎖經過WriteLock實現。
寫鎖是獨佔鎖,某一時刻只有一個線程能夠獲取該鎖。
寫鎖是可重入鎖,若是當前線程已經獲取該鎖,再次獲取只是簡單地把可重入次數+1後直接返回。
// ReentrantReadWriteLock.WriteLock#lock public static class WriteLock implements Lock, java.io.Serializable { private final Sync sync; public void lock() { sync.acquire(1); } } // AQS # acquire public final void acquire(int arg) { // 調用子類實現的tryAcquire,若是位false,則加入阻塞隊列,阻塞 if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) selfInterrupt(); } // ReentrantReadWriteLock.Sync#tryAcquire abstract static class Sync extends AbstractQueuedSynchronizer { protected final boolean tryAcquire(int acquires) { /* * Walkthrough: * 1. If read count nonzero or write count nonzero * and owner is a different thread, fail. * 2. If count would saturate, fail. (This can only * happen if count is already nonzero.) * 3. Otherwise, this thread is eligible for lock if * it is either a reentrant acquire or * queue policy allows it. If so, update state * and set owner. */ Thread current = Thread.currentThread(); int c = getState(); int w = exclusiveCount(c); // c != 0表示讀鎖或者寫鎖已經被某個線程獲取了 if (c != 0) { // c != 0 && w == 0表示有線程獲取了讀鎖,share count此時不爲0。 // c != 0 && w != 0而且當前線程不是寫鎖擁有者,返回false // 意思是隻要有讀鎖或寫鎖被佔用,此次獲取寫鎖就會失敗 if (w == 0 || current != getExclusiveOwnerThread()) return false; //走到這裏說明當前線程就是已經獲取寫鎖的,判斷可重入的次數是否超過了最大值 if (w + exclusiveCount(acquires) > MAX_COUNT) throw new Error("Maximum lock count exceeded"); // 設置可重入的次數,不須要CAS,由於走到這裏必然是寫鎖重入 setState(c + acquires); return true; } // 走到這,表示 c==0,此時爲第一個線程嘗試獲取寫鎖。 // 若是寫鎖不須要block,進行cas操做,成功則表示獲取成功 if (writerShouldBlock() || !compareAndSetState(c, c + acquires)) return false; // 通過前面的步驟以後,到這一步,才設置鎖的持有者爲當前線程 setExclusiveOwnerThread(current); return true; } }
writerShouldBlock方法實現,公平與非公平有差別:
static final class FairSync extends Sync { private static final long serialVersionUID = -2274990926593161451L; final boolean writerShouldBlock() { // 返回是否存在前驅節點,會先看看前面有沒有在排隊的 return hasQueuedPredecessors(); } } static final class NonfairSync extends Sync { private static final long serialVersionUID = -8159625535654395037L; // 老是返回false,直接去cas final boolean writerShouldBlock() { return false; // writers can always barge } }
很明顯了,對於非公平鎖來講,該方法永遠返回false,表示必定會且直接會走到compareAndSetState(c, c + acquires)
這一步,經過CAS嘗試獲取寫鎖,獲取成功就設置狀態,以後當前線程會被設置爲鎖的持有者,失敗則返回false。
意思是:非公平模式下,會直接嘗試cas去搶這個寫鎖,搶不到再排隊;而對於公平模式來講,若是阻塞隊列中,當前線程存在前驅節點,就放棄CAS爭奪寫鎖的過程。
相似於ReentrantLock的lockInterruptibly()方法,當其餘線程調用了該線程的interrupt()方法中斷了當前線程時,當前線程就會拋出InterruptedException異常。
public void lockInterruptibly() throws InterruptedException { sync.acquireInterruptibly(1); } //AQS public final void acquireInterruptibly(int arg) throws InterruptedException { if (Thread.interrupted()) throw new InterruptedException(); if (!tryAcquire(arg)) doAcquireInterruptibly(arg); }
嘗試獲取寫鎖,若是當前沒有其餘線程持有寫鎖或讀鎖,則當前線程獲取寫鎖會成功,返回true。
若是當前已經有其餘線程持有寫鎖或讀鎖則該方法直接返回false,且當前線程並不會被阻塞。
若是當前線程已經持有了該寫鎖則簡單增長AQS的狀態值後直接返回true。
public boolean tryLock( ) { return sync.tryWriteLock(); } // AQS final boolean tryWriteLock() { Thread current = Thread.currentThread(); int c = getState(); if (c != 0) { int w = exclusiveCount(c); if (w == 0 || current != getExclusiveOwnerThread()) return false; if (w == MAX_COUNT) throw new Error("Maximum lock count exceeded"); } if (!compareAndSetState(c, c + 1)) return false; setExclusiveOwnerThread(current); return true; }
其實和lock方法的邏輯大差不大,只是採用lock方法的非公平鎖邏輯。
相似於ReentrantLock的tryLock(long timeout,TimeUnit unit)方法。
嘗試獲取寫鎖,若是獲取失敗會將當前線程掛起指定時間,時間到了以後當前線程被激活,若是仍是沒有獲取到鎖,就返回false。
另外,該方法會對中斷進行的響應,若是其餘線程調用了當前線程的interrupt()方法,響應中斷,拋出異常。
嘗試釋放鎖,若是當前線程持有該鎖,調用該方法會讓該線程對該線程持有的AQS狀態減1,若是減1以後當前狀態值爲0,則當前線程會釋放該鎖。
若是當前線程沒有持有該鎖而調用了該方法,拋出IllegalMonitorStateException異常。
public void lock() { sync.acquireShared(1); } // AQS public final boolean release(int arg) { // 嘗試釋放鎖 if (tryRelease(arg)) { Node h = head; // 若是釋放成功,叫醒後繼節點 if (h != null && h.waitStatus != 0) unparkSuccessor(h); return true; } return false; } // ReentrantReadWriteLock.Sync#tryAcquire abstract static class Sync extends AbstractQueuedSynchronizer { protected final boolean tryRelease(int releases) { // 當前線程沒有持有該鎖而調用了該方法 if (!isHeldExclusively()) throw new IllegalMonitorStateException(); int nextc = getState() - releases; // 判斷一下是否是須要釋放鎖了 boolean free = exclusiveCount(nextc) == 0; // 清空一下 if (free) setExclusiveOwnerThread(null); // state沒有到0,僅僅是設置state而已 setState(nextc); // 若是寫鎖所有釋放,返回true,上面的方法就會喚醒以後的節點 return free; } }
ReentrantReadWriteLock中的讀鎖經過ReadLock實現,ps:讀鎖的獲取與釋放相對於寫鎖來講,較爲複雜。
// ReentrantReadWriteLock.ReadLock#lock public static class ReadLock implements Lock, java.io.Serializable { private final Sync sync; public void lock() { sync.acquireShared(1); } } // AQS # acquireShared public final void acquireShared(int arg) { // 調用子類實現的tryAcquireShared,若是爲false,則加入阻塞隊列,阻塞 if (tryAcquireShared(arg) < 0) doAcquireShared(arg); } // ReentrantReadWriteLock.Sync#tryAcquire abstract static class Sync extends AbstractQueuedSynchronizer { protected final int tryAcquireShared(int unused) { /* * Walkthrough: * 1. If write lock held by another thread, fail. * 2. Otherwise, this thread is eligible for * lock wrt state, so ask if it should block * because of queue policy. If not, try * to grant by CASing state and updating count. * Note that step does not check for reentrant * acquires, which is postponed to full version * to avoid having to check hold count in * the more typical non-reentrant case. * 3. If step 2 fails either because thread * apparently not eligible or CAS fails or count * saturated, chain to version with full retry loop. */ Thread current = Thread.currentThread(); // 獲取當前狀態值 int c = getState(); if (exclusiveCount(c) != 0 && // 說明有線程持有寫鎖 getExclusiveOwnerThread() != current) // 而且不是當前線程持有寫鎖 return -1; // 失敗 //----- 這裏提一嘴,上面若是持有寫鎖的是本身,就仍是能夠獲取讀鎖的 -----// // 獲取讀鎖計數 int r = sharedCount(c); // 讀鎖獲取是否須要阻塞,若不成功將會進入fullTryAcquireShared進行重試 if (!readerShouldBlock() && r < MAX_COUNT && // 判斷讀鎖獲取次數是否溢出 // 嘗試將高16位+1,低16位不變,若是獲取成功則表示獲取到了讀鎖 compareAndSetState(c, c + SHARED_UNIT)) { // ----- 能走到這裏表示當前線程獲取讀鎖成功 ----- // // r==0表示第一個線程獲取讀鎖 ,也有可能以前有線程但被釋放了,當前天然就是第一個啦 if (r == 0) { firstReader = current; // 記錄一下firstReader【每次將讀鎖獲取次數從0變成1】 firstReaderHoldCount = 1; // 記錄一下持有的讀鎖數量 1 // 來到這裏 c != 0 且 firstReader == current ,表示firstReader可重入了 } else if (firstReader == current) { firstReaderHoldCount++; // 直接計數加1 } else { // cachedHoldCounter 使用來緩存最後一個獲取讀鎖的線程的,以後用rh表示 HoldCounter rh = cachedHoldCounter; // 若是rh還沒緩存 或者 存的不是當前線程 if (rh == null || rh.tid != getThreadId(current)) // 那就更新一下cachedHoldCounter 爲當前線程的HoldCounter cachedHoldCounter = rh = readHolds.get(); // 走到這裏,說明緩存的是當前線程,可是count是0 else if (rh.count == 0) readHolds.set(rh); // count 加1 rh.count++; } return 1; // 大於0表示獲取到了共享鎖 } // 相似tryAcquireShared,自旋獲取,這裏失敗的話,就得進阻塞隊列去了嗷 return fullTryAcquireShared(current); } }
readerShouldBlock方法實現,公平與非公平有差別:
static final class FairSync extends Sync { final boolean readerShouldBlock() { // 看看阻塞隊列中是否已經有其餘元素在排隊 return hasQueuedPredecessors(); } } static final class NonfairSync extends Sync { final boolean readerShouldBlock() { /* As a heuristic to avoid indefinite writer starvation, * block if the thread that momentarily appears to be head * of queue, if one exists, is a waiting writer. This is * only a probabilistic effect since a new reader will not * block if there is a waiting writer behind other enabled * readers that have not yet drained from the queue. */ // 判斷阻塞隊列中 第一個節點是不是來獲取寫鎖的,若是是的話,讓這個寫鎖先來。 return apparentlyFirstQueuedIsExclusive(); } }
具體看下非公平鎖的實現,apparentlyFirstQueuedIsExclusive方法:
final boolean apparentlyFirstQueuedIsExclusive() { Node h, s; return (h = head) != null && // 隊列是否爲空 (s = h.next) != null && // 是否存在第一個元素 !s.isShared() && // 第一個元素是否正在嘗試獲取寫鎖 s.thread != null; // 該元素的線程是否爲null }
聯繫起來解釋:
!readerShouldBlock()
:若是隊列裏面存在一個元素,判斷第一個元素是否是正在嘗試獲取寫鎖,若是是的話,這個讓這個元素先來,它的優先級很高。r < MAX_COUNT
:判斷當前獲取讀鎖的線程是否達到最大值。compareAndSetState(c, c + SHARED_UNIT)
:執行CAS操做將AQS狀態值的高16位值增長1。其實就是:看看隊列裏面的第一個節點是否是在嘗試獲取寫鎖,若是是的話,就讓他先來。若是你在獲取讀鎖,那很差意思,乖乖地去CAS吧,看誰能搶到。
若是沒有獲取到讀鎖,會怎麼辦呢?進入fullTryAcquireShared邏輯看看:
/** * Full version of acquire for reads, that handles CAS misses * and reentrant reads not dealt with in tryAcquireShared. */ final int fullTryAcquireShared(Thread current) { /* * 這段代碼和tryAcquireShared部分冗餘,但整體更加簡單 */ HoldCounter rh = null; // 自旋 for (;;) { int c = getState(); // 已經有線程獲取了寫鎖 if (exclusiveCount(c) != 0) { // 且獲取寫鎖的線程不是當前線程,那就直接進隊,若是是當前線程,走到cas去,鎖降級的過程 if (getExclusiveOwnerThread() != current) return -1; } else if (readerShouldBlock()) { // 走到這一步,表示寫鎖沒被佔有,且阻塞隊列中有其餘線程在等待 // firstReader線程重入讀鎖,直接快進到下面的cas if (firstReader == current) { // assert firstReaderHoldCount > 0; } else { if (rh == null) { rh = cachedHoldCounter; // cachedHoldCounter 沒有緩存或緩存的不是當前線程 if (rh == null || rh.tid != getThreadId(current)) { // 若是當前線程歷來沒有初始化ThreadLocal中的值,get方法會進行初始化 rh = readHolds.get(); // 表示上一行代碼是初始化的,執行remove if (rh.count == 0) readHolds.remove(); } } // 排隊 if (rh.count == 0) return -1; } } if (sharedCount(c) == MAX_COUNT) throw new Error("Maximum lock count exceeded"); // cas操做成功,表示獲取讀鎖了,接下來設置firstReader或firstReaderHoldCount if (compareAndSetState(c, c + SHARED_UNIT)) { if (sharedCount(c) == 0) { firstReader = current; firstReaderHoldCount = 1; } else if (firstReader == current) { firstReaderHoldCount++; } else { // 將cachedHoldCounter設置爲當前線程 if (rh == null) rh = cachedHoldCounter; if (rh == null || rh.tid != getThreadId(current)) rh = readHolds.get(); else if (rh.count == 0) readHolds.set(rh); rh.count++; cachedHoldCounter = rh; // cache for release } return 1; } } }
接下來讀鎖的幾個方法和寫鎖其實差不太多,源碼就不貼了,感興趣的小夥伴能夠本身看看。
相似於lock()方法,區別在於,該方法可以中斷響應,當其餘線程調用該線程的interrupt()方法中斷了當前線程時,當前線程拋出InterruptedException異常。
嘗試讀取鎖,若是當前沒有其餘線程持有寫鎖,則當前線程會獲取讀鎖成功,返回true。
若是當前已經有其餘線程持有寫鎖,則直接返回false,不會阻塞。
若是當前線程已經持有了該讀鎖,則利用AQS將state的高16位加1,返回true。
相似於tryLock,不一樣的是,設定了超時時間,超時時間到了,若是沒有讀取到讀鎖,直接返回false。
可中斷響應,當其餘線程調用該線程的interrupt()方法中斷了當前線程時,當前線程拋出InterruptedException異常。
public void unlock() { sync.releaseShared(1); } // AQS public final boolean releaseShared(int arg) { // 若是tryReleaseShared返回true,釋放一個因爲獲取寫鎖而被阻塞的線程 if (tryReleaseShared(arg)) { doReleaseShared(); return true; } return false; } abstract static class Sync extends AbstractQueuedSynchronizer { protected final boolean tryReleaseShared(int unused) { Thread current = Thread.currentThread(); if (firstReader == current) { // 若是firstReaderHoldCount爲1,此次解鎖以後,就會變成0了,將firstReader設置爲null if (firstReaderHoldCount == 1) firstReader = null; else // 不然減1就能夠 firstReaderHoldCount--; } else { // 判斷cacheHoldCounter是否緩存的是當前線程,若是不是的話,須要從ThreadLocal中取。 HoldCounter rh = cachedHoldCounter; if (rh == null || rh.tid != getThreadId(current)) rh = readHolds.get(); int count = rh.count; if (count <= 1) { // 再也不持有鎖了,調用remove,防止內存泄露 readHolds.remove(); if (count <= 0) throw unmatchedUnlockException(); } --rh.count; } // 無限循環,保證CAS操做成功 for (;;) { // 獲取狀態值 int c = getState(); int nextc = c - SHARED_UNIT; // CAS 操做更新狀態值。CAS操做若是不成功,會一直循環 if (compareAndSetState(c, nextc)) // 若是更新成功,查看當前狀態值是否爲0,若是爲0說明已經沒有讀線程佔用讀鎖 // 若是不爲0,則說明還有其餘線程持有讀鎖,返回false return nextc == 0; } } }
鎖降級就意味着寫鎖是能夠降級爲讀鎖的,可是須要遵循先獲取寫鎖、獲取讀鎖在釋放寫鎖的次序。注意若是當前線程先獲取寫鎖,而後釋放寫鎖,再獲取讀鎖這個過程不能稱之爲鎖降級,鎖降級必定要遵循那個次序。
注意,做者Doug Lea並無說寫鎖更爲高級,若是有線程持有讀鎖,那麼寫鎖獲取也須要等待,但源碼中確實能夠看出給寫鎖一些特殊照顧,如在非公平模式下,爲了提升吞吐量,若是發現第一個節點是獲取寫鎖的線程,直接獲取成功。
鎖降級的部分,源碼中是這樣體現的:
int c = getState(); // 已經有線程獲取了寫鎖 if (exclusiveCount(c) != 0) { // 且獲取寫鎖的線程不是當前線程,那就直接進隊,若是是當前線程,走到cas去,鎖降級的過程 if (getExclusiveOwnerThread() != current) return -1;
鎖降級中讀鎖的獲取是否必要?
假如當前線程 A 不獲取讀鎖而是直接釋放了寫鎖,這個時候另一個線程 B 獲取了寫鎖,那麼這個線程 B 對數據的修改是不會對當前線程 A 可見的。
若是獲取了讀鎖,則線程B在獲取寫鎖過程當中判斷若是有讀鎖尚未釋放則會被阻塞,只有當前線程 A 釋放讀鎖後,線程 B 纔會獲取寫鎖成功。
ReentrantReadWriteLock底層使用AQS實現,利用AQS的狀態值的高16位表示獲取到讀鎖的個數,低16位標識獲取到寫鎖的線程的可重入次數,經過CAS對其進行操做實現讀寫分離,適用於讀多寫少的場景。
ReentrantReadWriteLock的三個特性:
讀寫鎖:讀寫鎖容許同一時刻有多個讀線程訪問,但在寫線程訪問時,全部的讀線程和其餘寫線程均被阻塞。
方騰飛:《Java併發編程的藝術》
DougLea:《Java併發編程實戰》