您好,登錄后才能下訂單哦!
本篇內(nèi)容主要講解“Java多線程并發(fā)ReentrantReadWriteLock源碼分析”,感興趣的朋友不妨來看看。本文介紹的方法操作簡單快捷,實(shí)用性強(qiáng)。下面就讓小編來帶大家學(xué)習(xí)“Java多線程并發(fā)ReentrantReadWriteLock源碼分析”吧!
ReentrantReadWriteLock 是基于 AbstractQueuedSynchronizer 并實(shí)現(xiàn)了 ReadWriteLock 接口實(shí)現(xiàn)的一個(gè)鎖機(jī)制。ReadWriteLock 定義了讀寫鎖的特性:
public interface ReadWriteLock { /** * Returns the lock used for reading. */ Lock readLock(); /** * Returns the lock used for writing. */ Lock writeLock(); }
ReadWriteLock 中定義了獲取兩種鎖的方式,一個(gè)用于獲取讀鎖、一個(gè)用于獲取寫鎖。只要沒有持有寫鎖的線程在執(zhí)行,讀鎖可以同時(shí)被多個(gè)嘗試讀操作的線程持有,而寫鎖是排他鎖。
與互斥鎖相比,讀寫鎖在訪問共享數(shù)據(jù)時(shí)允許更高級的并發(fā)特性,即每次只有一個(gè)線程可以執(zhí)行寫操作,并且在沒有寫操作時(shí)其他線程可以并發(fā)讀取共享數(shù)據(jù)。從讀操作的效率來看,如果是互斥鎖每次只能一個(gè)線程執(zhí)行讀寫操作,而讀寫鎖可以多個(gè)線程讀,寫操作時(shí)才互斥,所以讀寫鎖的執(zhí)行效率更高。
前面的內(nèi)容介紹了讀寫鎖的含義和優(yōu)勢,接下來分析 Java 并發(fā)包中對它的實(shí)現(xiàn) ReentrantReadWriteLock 。
public class ReentrantReadWriteLock implements ReadWriteLock, java.io.Serializable { abstract static class Sync extends AbstractQueuedSynchronizer { static final class HoldCounter static final class ThreadLocalHoldCounter extends ThreadLocal<HoldCounter> } static final class NonfairSync extends Sync static final class FairSync extends Sync public static class ReadLock implements Lock, java.io.Serializable public static class WriteLock implements Lock, java.io.Serializable }
ReentrantReadWriteLock 實(shí)現(xiàn)了讀寫鎖接口 ReadWriteLock 和序列化接口 Serializable 。
它有一個(gè)抽象靜態(tài)內(nèi)部類 Sync ,Sync 是 AQS 的抽象子類,Sync 有兩個(gè)靜態(tài)實(shí)現(xiàn) NonfairSync 和 FairSync ,這部分是鎖邏輯的核心內(nèi)容;Sync 還有兩個(gè)內(nèi)部數(shù)據(jù)結(jié)構(gòu)類 HoldCounter 和 ThreadLocalHoldCounter 。
ReadLock 和 WriteLock 分別對應(yīng)了讀鎖和寫鎖,它們都實(shí)現(xiàn)了 Lock 接口和序列號接口 Serializable 。它們是 ReentrantReadWriteLock 中對不同操作的鎖類型的實(shí)現(xiàn),使用了裝飾模式,本質(zhì)上還是通過 Sync 的能力實(shí)現(xiàn)的。
核心邏輯是來自于 Sync 及其兩個(gè)實(shí)現(xiàn),Sync 繼承自 AbstractQueuedSynchronizer ,自身有兩個(gè)內(nèi)部類 HoldCounter 和 ThreadLocalHoldCounter 。
static final class HoldCounter { int count; // initially 0 // Use id, not reference, to avoid garbage retention final long tid = LockSupport.getThreadId(Thread.currentThread()); }
HoldCounter 是一個(gè)計(jì)數(shù)器,count
用來記錄當(dāng)前線程擁有讀鎖的數(shù)量,即讀鎖的重入次數(shù);tid
用來記錄當(dāng)前線程唯一 ID 。
Sync 有一個(gè) cachedHoldCounter
屬性,用來做緩存效果,避免每次都通過 ThreadLocal 去讀取數(shù)據(jù)。
static final class ThreadLocalHoldCounter extends ThreadLocal<HoldCounter> { public HoldCounter initialValue() { return new HoldCounter(); } }
ThreadLocalHoldCounter 重寫了 ThreadLocal 的 initialValue()
,在 ThreadLocal 沒有進(jìn)行過 set 數(shù)據(jù)的情況下,默認(rèn)讀取到的值都來自于這個(gè)方法,也就是配合 ThreadLocal 使用,默認(rèn)值返回一個(gè)新的 HoldCounter 實(shí)例。
在 Sync 中,有一個(gè)屬性 readHolds
,它的類型是 ThreadLocalHoldCounter ,用來做當(dāng)前線程讀鎖重入計(jì)數(shù)器的 ThreadLocal 包裝,便于線程讀取自己的讀鎖重入計(jì)數(shù)器。
Sync 中定義的屬性包括:
abstract static class Sync extends AbstractQueuedSynchronizer { // 高16位為讀鎖,低16位為寫鎖 static final int SHARED_SHIFT = 16; // 讀鎖單位 static final int SHARED_UNIT = (1 << SHARED_SHIFT); // 1 * 2^16 = 65536 // 讀鎖最大數(shù)量 static final int MAX_COUNT = (1 << SHARED_SHIFT) - 1; // 2^16 - 1 // 寫鎖最大數(shù)量 static final int EXCLUSIVE_MASK = (1 << SHARED_SHIFT) - 1; // 2^16 - 1 獨(dú)占標(biāo)記 // 當(dāng)前線程讀鎖重入次數(shù)。當(dāng)持有讀鎖的線程數(shù)量下降到0時(shí)刪除。 private transient ThreadLocalHoldCounter readHolds; // 緩存對象,避免每次都去從 ThreadLocal 查找。 private transient HoldCounter cachedHoldCounter; // 第一個(gè)獲取讀鎖線程 private transient Thread firstReader; // 第一個(gè)讀鎖線程重入讀鎖的計(jì)數(shù) private transient int firstReaderHoldCount; // ... }
Sync() { readHolds = new ThreadLocalHoldCounter(); setState(getState()); // ensures visibility of readHolds }
Sync 初始化方法創(chuàng)建了 ThreadLocalHoldCounter 并重新設(shè)置了 State ,為什么要重新設(shè)置呢?因?yàn)檫@里要讀取當(dāng)前線程最新的同步狀態(tài)并重新設(shè)置,獲取實(shí)時(shí)的同步狀態(tài)。
Sync 的關(guān)鍵方法包括:
abstract static class Sync extends AbstractQueuedSynchronizer { // 并發(fā)計(jì)數(shù) static int sharedCount(int c) static int exclusiveCount(int c) // 阻塞檢查 abstract boolean readerShouldBlock(); abstract boolean writerShouldBlock(); // 獲取和釋放寫鎖 @ReservedStackAccess protected final boolean tryRelease(int releases) @ReservedStackAccess protected final boolean tryAcquire(int acquires) // 獲取和釋放讀鎖 @ReservedStackAccess protected final boolean tryReleaseShared(int unused) @ReservedStackAccess protected final int tryAcquireShared(int unused) final int fullTryAcquireShared(Thread current) // 嘗試加讀寫鎖 @ReservedStackAccess final boolean tryWriteLock() @ReservedStackAccess final boolean tryReadLock() // ... }
首先是兩個(gè)靜態(tài)方法 sharedCount(int c)
和 exclusiveCount(int c)
:
/** 表示共享持有的數(shù)量。 */ static int sharedCount(int c) { return c >>> SHARED_SHIFT; } // 無符號右移,高位補(bǔ) 0 /** 表示獨(dú)占持有的數(shù)量。 */ static int exclusiveCount(int c) { return c & EXCLUSIVE_MASK; }
參數(shù) c 是 AQS 中的 state,根據(jù) state 進(jìn)行位運(yùn)算。這兩個(gè)方法可以根據(jù)鎖自身的狀態(tài)解析出持有讀寫鎖的數(shù)量。
sharedCount
,表示占有讀鎖的線程數(shù)量。直接將 AQS 中的 state 右移 16 位,高位補(bǔ) 0,就可以得到讀鎖的線程數(shù)量,因?yàn)?state 的高十六位表示讀鎖,對應(yīng)的低十六位表示寫鎖數(shù)量。
exclusiveCount
,表示占有寫鎖的線程數(shù)量。直接將 AQS 的 state 和 (2^16 - 1) 做與運(yùn)算,其等效于將 state 模上 2^16 。寫鎖數(shù)量由 state 的低十六位表示。
第二組方法是 readerShouldBlock
和 writerShouldBlock
,用來檢查當(dāng)前的讀鎖/寫鎖是否會(huì)造成當(dāng)前線程阻塞。
// 獲取和釋放對公平鎖和非公平鎖使用相同的代碼,不同點(diǎn)在于但在隊(duì)列非空時(shí)是否/如何允許碰撞。 // 如果當(dāng)前線程在嘗試獲取讀鎖時(shí),并且在其他符合條件的線程也在嘗試獲取讀鎖,由于策略其他等待線程占用了讀鎖,當(dāng)前線程應(yīng)該阻塞,則返回true。 abstract boolean readerShouldBlock(); // 如果當(dāng)前線程在嘗試獲取寫鎖時(shí),并且在其他符合條件的線程也在嘗試獲取寫鎖,由于策略其他等待線程占用了寫鎖,當(dāng)前線程應(yīng)該阻塞,則返回true。 abstract boolean writerShouldBlock();
這兩個(gè)方法的實(shí)現(xiàn)在 Sync 的子類中 -- 公平策略實(shí)現(xiàn) FairSync 和非公平策略實(shí)現(xiàn) NonfairSync。
// 非公平策略 static final class NonfairSync extends Sync { private static final long serialVersionUID = -8159625535654395037L; final boolean writerShouldBlock() { return false; // 正在持有寫鎖的線程永不阻塞 } final boolean readerShouldBlock() { return apparentlyFirstQueuedIsExclusive(); } } // 公平策略 static final class FairSync extends Sync { private static final long serialVersionUID = -2274990926593161451L; final boolean writerShouldBlock() { return hasQueuedPredecessors(); } final boolean readerShouldBlock() { return hasQueuedPredecessors(); } }
公平鎖策略和非公平鎖策略的實(shí)現(xiàn),本質(zhì)上的不同是這兩個(gè)方法的實(shí)現(xiàn)。
NonfairSync 中,執(zhí)行寫操作的線程是否應(yīng)該進(jìn)入阻塞狀態(tài)的判斷,直接是 false ,這是因?yàn)榉枪讲呗韵?,如果?dāng)前自身已經(jīng)擁有了寫鎖,直接重入,以獨(dú)占的方式繼續(xù)運(yùn)行(所以是不公平的)。
執(zhí)行讀操作的線程是否會(huì)阻塞,是通過 apparentlyFirstQueuedIsExclusive()
判斷的,這個(gè)方法是 AQS 中的方法:
final boolean apparentlyFirstQueuedIsExclusive() { Node h = head, s = head.next; return h != null && s != null && !(s instanceof SharedNode) && s.waiter != null; }
這個(gè)方法的作用是,CLH 隊(duì)列中的頭節(jié)點(diǎn)和它的的 next 都存在的情況下,如果 next 節(jié)點(diǎn)不是 SharedNode ,且它的關(guān)聯(lián)線程不為空的情況(即下一個(gè)鎖不是共享鎖,共享鎖在讀寫鎖里就是讀鎖)的情況,會(huì)導(dǎo)致當(dāng)前執(zhí)行讀操作的線程進(jìn)入阻塞狀態(tài),確保寫操作的互斥特性。
FairSync 中,讀寫執(zhí)行線程是否應(yīng)該進(jìn)入阻塞狀態(tài)都是根據(jù) hasQueuedPredecessors()
方法判斷的:
public final boolean hasQueuedPredecessors() { Thread first = null; Node h = head, s = h.next; if (h != null && (s == null || (first = s.waiter) == null || s.prev == null)) first = getFirstQueuedThread(); // retry via getFirstQueuedThread return first != null && first != Thread.currentThread(); } public final Thread getFirstQueuedThread() { Thread first = null, w; Node h, s; if ((h = head) != null && ((s = h.next) == null || (first = s.waiter) == null || s.prev == null)) { // traverse from tail on stale reads for (Node p = tail, q; p != null && (q = p.prev) != null; p = q) if ((w = p.waiter) != null) first = w; } return first; }
hasQueuedPredecessors()
對 head 節(jié)點(diǎn)和它的 next 節(jié)點(diǎn)進(jìn)行空檢查,并檢查下一個(gè)節(jié)點(diǎn)的執(zhí)行線程和 prev 指針是否有值,滿足條件的情況下通過 getFirstQueuedThread()
方法獲取到隊(duì)列中第一個(gè)節(jié)點(diǎn)關(guān)聯(lián)的線程。最終返回的結(jié)過是檢查這個(gè)線程不等于當(dāng)前線程。
如果存在等待隊(duì)列第一個(gè)等待執(zhí)行的線程,那么就優(yōu)先執(zhí)行這個(gè)線程。也就是說,不管當(dāng)前線程是擁有讀鎖還是寫鎖,都優(yōu)先執(zhí)行等待隊(duì)列第一個(gè)未執(zhí)行節(jié)點(diǎn),這里就能體現(xiàn)出公平,即優(yōu)先執(zhí)行等待隊(duì)列中頭一個(gè)等待的節(jié)點(diǎn)所關(guān)聯(lián)的線程。
這一組方法是整個(gè) Sync 的核心邏輯,也是加解鎖核心邏輯。
tryRelease
:
@ReservedStackAccess protected final boolean tryRelease(int releases) { if (!isHeldExclusively()) // 不是獨(dú)占持有鎖的情況,直接拋出異常。 throw new IllegalMonitorStateException(); int nextc = getState() - releases; // AQS 當(dāng)前鎖狀態(tài) - releases = 新的鎖狀態(tài) boolean free = exclusiveCount(nextc) == 0; // 根據(jù)新的鎖狀態(tài)獲取到獨(dú)占寫鎖的數(shù)量 == 0 if (free) setExclusiveOwnerThread(null); // 持有寫鎖的線程數(shù)為0,更新當(dāng)前獨(dú)占線程引用 setState(nextc); // 無論是不是解鎖了,都要更新鎖狀態(tài) return free; // 最后返回鎖是否已經(jīng)可用了 }
tryRelease(int releases)
用來嘗試釋放寫鎖。
它的邏輯如下圖:
tryAcquire
:
@ReservedStackAccess protected final boolean tryAcquire(int acquires) { /* * 工作流程: * 1. 如果寫鎖計(jì)數(shù)非零或所有者是不同的線程,則失敗。 * 2. 如果寫鎖計(jì)數(shù)超過最大數(shù)量,失?。ㄟ@只發(fā)生在計(jì)數(shù)非 0 的情況)。 * 3. 否則,如果這個(gè)線程是可重入的獲取方式或者隊(duì)列策略允許的話,它就有資格獲得鎖。 * 如果是,更新狀態(tài)并設(shè)置 owner。 */ Thread current = Thread.currentThread(); // 當(dāng)前線程 int c = getState(); // 當(dāng)前鎖狀態(tài) int w = exclusiveCount(c); // 計(jì)算擁有寫鎖的線程數(shù)量 if (c != 0) { // 0 是鎖可用狀態(tài),當(dāng)前狀態(tài)表面鎖狀態(tài)為被持有。 if (w == 0 || current != getExclusiveOwnerThread()) // 對應(yīng) 【1】 的情況,寫線程數(shù)量為0或者當(dāng)前線程沒有占有獨(dú)占資源 return false; if (w + exclusiveCount(acquires) > MAX_COUNT) // 對應(yīng)【2】的情況, 判斷是否超過最高寫線程數(shù)量 throw new Error("Maximum lock count exceeded"); // 重入獲取寫鎖 setState(c + acquires); return true; } if (writerShouldBlock() || !compareAndSetState(c, c + acquires)) // 是否應(yīng)該阻塞或更新狀態(tài)是否成功,失敗直接 return false; return false; setExclusiveOwnerThread(current); // 設(shè)置當(dāng)前為持有鎖的線程。 return true; }
此函數(shù)用于獲取寫鎖,首先會(huì)獲取 state ,判斷 state 是否為0。
若為0,表示此時(shí)沒有讀鎖線程,再判斷寫線程是否應(yīng)該被阻塞,而在非公平策略下總是不會(huì)被阻塞,在公平策略下會(huì)進(jìn)行判斷(判斷同步隊(duì)列中是否有等待時(shí)間更長的線程,若存在,則需要被阻塞,否則,無需阻塞),之后在設(shè)置狀態(tài)state,然后返回true。若state不為0,則表示此時(shí)存在讀鎖或?qū)戞i線程,若寫鎖線程數(shù)量為0或者當(dāng)前線程為獨(dú)占鎖線程,則返回false,表示不成功,否則,判斷寫鎖線程的重入次數(shù)是否大于了最大值,若是,則拋出異常,否則,設(shè)置狀態(tài)state,返回true,表示成功。
其函數(shù)流程圖如下:
tryReleaseShared
:
@ReservedStackAccess protected final boolean tryReleaseShared(int unused) { Thread current = Thread.currentThread(); // 當(dāng)前線程 if (firstReader == current) { // 當(dāng)前線程是否是第一個(gè)讀線程 // assert firstReaderHoldCount > 0; if (firstReaderHoldCount == 1) firstReader = null; // 釋放線程引用 else firstReaderHoldCount--; // 當(dāng)前線程重入次數(shù)自減 } else { HoldCounter rh = cachedHoldCounter; // 獲取當(dāng)前線程的重入讀鎖的次數(shù) if (rh == null || rh.tid != LockSupport.getThreadId(current)) rh = readHolds.get(); int count = rh.count; if (count <= 1) { readHolds.remove(); if (count <= 0) throw unmatchedUnlockException(); } --rh.count; } // 死循環(huán)直到更新狀態(tài)成功 for (;;) { int c = getState(); int nextc = c - SHARED_UNIT; if (compareAndSetState(c, nextc)) // Releasing the read lock has no effect on readers, // but it may allow waiting writers to proceed if // both read and write locks are now free. return nextc == 0; } }
tryAcquireShared
:
@ReservedStackAccess 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) // 當(dāng)獨(dú)占線程不是當(dāng)前線程 return -1; int r = sharedCount(c); // 共享讀鎖的線程數(shù)量 // 檢查讀線程不應(yīng)該阻塞 and 持有讀鎖的線程數(shù)量小于 MAX_COUNT and 更新鎖狀態(tài)成功 if (!readerShouldBlock() && r < MAX_COUNT && compareAndSetState(c, c + SHARED_UNIT)) { if (r == 0) { // 第一個(gè)嘗試獲取讀鎖的線程 firstReader = current; firstReaderHoldCount = 1; } else if (firstReader == current) { // 第一個(gè)線程重入 firstReaderHoldCount++; } else { HoldCounter rh = cachedHoldCounter; // 無緩存 or 當(dāng)前線程不是計(jì)數(shù)器所在線程 if (rh == null || rh.tid != LockSupport.getThreadId(current)) cachedHoldCounter = rh = readHolds.get(); // 從 ThreadLocal 中讀取 else if (rh.count == 0) readHolds.set(rh); rh.count++; // 當(dāng)前線程獲取讀鎖次數(shù) + 1 } return 1; } return fullTryAcquireShared(current); }
最后執(zhí)行到了 fullTryAcquireShared
:
final int fullTryAcquireShared(Thread current) { /* * 這段代碼與 tryAcquireShared 中的部分代碼是冗余的,但總體上更簡單,因?yàn)樗粫?huì)使 * tryAcquireShared 在重試和懶加載讀鎖計(jì)數(shù)之間的交互復(fù)雜化。 */ HoldCounter rh = null; for (;;) { // 死循環(huán),不斷嘗試 int c = getState(); if (exclusiveCount(c) != 0) { // 獨(dú)占檢查是否是當(dāng)前線程 if (getExclusiveOwnerThread() != current) return -1; // 否則我們持有獨(dú)占鎖;這里的阻塞將導(dǎo)致死鎖。 } else if (readerShouldBlock()) { // 確保我們不是重入式地獲取讀鎖 if (firstReader == current) { // assert firstReaderHoldCount > 0; } else { // 不是重入的情況下,更新 HoldCounter if (rh == null) { rh = cachedHoldCounter; if (rh == null || rh.tid != LockSupport.getThreadId(current)) { rh = readHolds.get(); if (rh.count == 0) readHolds.remove(); } } if (rh.count == 0) return -1; } } // 共享讀鎖 == 最大數(shù)量,拋出異常 if (sharedCount(c) == MAX_COUNT) throw new Error("Maximum lock count exceeded"); // 是否能夠設(shè)置成功 if (compareAndSetState(c, c + SHARED_UNIT)) { if (sharedCount(c) == 0) { // 第一個(gè)線程 firstReader = current; firstReaderHoldCount = 1; } else if (firstReader == current) { // 重入 firstReaderHoldCount++; } else { // 其他情況 if (rh == null) rh = cachedHoldCounter; if (rh == null || rh.tid != LockSupport.getThreadId(current)) rh = readHolds.get(); else if (rh.count == 0) readHolds.set(rh); rh.count++; cachedHoldCounter = rh; // cache for release } return 1; } } }
這個(gè)方法的整體邏輯與 tryAcquireShared 基本相同。
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 lockInterruptibly() throws InterruptedException { sync.acquireSharedInterruptibly(1); } public boolean tryLock() { return sync.tryReadLock(); } public boolean tryLock(long timeout, TimeUnit unit) throws InterruptedException { return sync.tryAcquireSharedNanos(1, unit.toNanos(timeout)); } public void unlock() { sync.releaseShared(1); } public Condition newCondition() { throw new UnsupportedOperationException(); } // ... }
ReadLock 實(shí)現(xiàn)了 Lock 接口,代理調(diào)用到邏輯都是 Sync 中 Shared 組的核心方法。ReadLock 可以通過 readLock(): ReadLock
方法獲取到。
還有一點(diǎn)值得注意,newCondition()
方法直接拋出了異常,這是因?yàn)樽x鎖是一種共享鎖,不會(huì)導(dǎo)致互斥,所以也就不支持使用 Condition 控制阻塞與喚醒。
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 lockInterruptibly() throws InterruptedException { sync.acquireInterruptibly(1); } public boolean tryLock() { return sync.tryWriteLock(); } public boolean tryLock(long timeout, TimeUnit unit) throws InterruptedException { return sync.tryAcquireNanos(1, unit.toNanos(timeout)); } public void unlock() { sync.release(1); } public Condition newCondition() { return sync.newCondition(); } public String toString() { Thread o = sync.getOwner(); return super.toString() + ((o == null) ? "[Unlocked]" : "[Locked by thread " + o.getName() + "]"); } public boolean isHeldByCurrentThread() { return sync.isHeldExclusively(); } public int getHoldCount() { return sync.getWriteHoldCount(); } }
寫鎖本質(zhì)上也是代理 Sync 中的核心方法。
鎖降級指的是寫鎖降級為讀鎖,如果當(dāng)前線程擁有寫鎖,將其釋放然后再獲取讀鎖,這種操作過程不是鎖降級。鎖降級是指把線程當(dāng)前持有寫鎖,再去獲取讀鎖,隨后釋放寫鎖,這個(gè)流程稱為鎖降級。
public void processData() { readLock.lock(); if (!update) { // 必須先釋放讀鎖 readLock.unlock(); // 鎖降級從寫鎖獲取到開始 writeLock.lock(); try { if (!update) { // 準(zhǔn)備數(shù)據(jù)的流程(略) update = true; } readLock.lock(); } finally { writeLock.unlock(); } // 鎖降級完成,寫鎖降級為讀鎖 } try { // 使用數(shù)據(jù)的流程(略) } finally { readLock.unlock(); } }
鎖降級可以保證數(shù)據(jù)的可見性,如果再持有寫鎖的情況下,不先去獲取讀鎖,直接釋放寫鎖,再嘗試獲取讀鎖,這一系列操作中會(huì)有短暫的無鎖狀態(tài),此時(shí)如果有其他線程獲取了寫鎖并修改數(shù)據(jù),那么當(dāng)前線程就無法感知到數(shù)據(jù)更新,如果當(dāng)前線程先獲取了讀鎖,那么其他線程就會(huì)阻塞,直到當(dāng)前線程釋放讀鎖后才能獲取寫鎖進(jìn)行更新。
讀寫鎖 ReentrantReadWriteLock 不支持鎖升級,目的是保證數(shù)據(jù)的可見性,如果讀鎖已被多個(gè)線程獲取,其中任意線程成功獲取了寫鎖,并更新了數(shù)據(jù),那么這個(gè)更新對其他線程是不可見的,容易造成數(shù)據(jù)不一致問題。
到此,相信大家對“Java多線程并發(fā)ReentrantReadWriteLock源碼分析”有了更深的了解,不妨來實(shí)際操作一番吧!這里是億速云網(wǎng)站,更多相關(guān)內(nèi)容可以進(jìn)入相關(guān)頻道進(jìn)行查詢,關(guān)注我們,繼續(xù)學(xué)習(xí)!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。