Java中ReentrantLock和ReentrantReadWriteLock的原理
ReentrantLock 原理
概念
基于AQS實(shí)現(xiàn)的可重入鎖實(shí)現(xiàn)類。
核心變量和構(gòu)造器
public class ReentrantLock implements Lock, java.io.Serializable { ? ?private final Sync sync; ? ?public ReentrantLock() { ? ? ? ?// 默認(rèn)為非公平鎖。為何默認(rèn)為非公平鎖?因?yàn)橥ㄟ^大量測(cè)試下來,發(fā)現(xiàn)非公平鎖的性能優(yōu)于公平鎖 ? ? ? ?sync = new NonfairSync(); ? } ? ?public ReentrantLock(boolean fair) { ? ? ? ?// 由fair變量來表明選擇鎖類型 ? ? ? ?sync = fair ? new FairSync() : new NonfairSync(); ? } ? ?abstract static class Sync extends AbstractQueuedSynchronizer { ? ? ? ?abstract void lock(); ? ? ? ?// 非公平鎖標(biāo)準(zhǔn)獲取鎖方法 ? ? ? ?final boolean nonfairTryAcquire(int acquires) { ? ? ? ? ? ?final Thread current = Thread.currentThread(); ? ? ? ? ? ?int c = getState(); ? ? ? ? ? ?// 當(dāng)執(zhí)行到這里時(shí),正好獲取所得線程釋放了鎖,那么可以嘗試搶鎖 ? ? ? ? ? ?if (c == 0) { ? ? ? ? ? ? ? ?// 繼續(xù)搶鎖,不看有沒有線程排隊(duì) ? ? ? ? ? ? ? ?if (compareAndSetState(0, acquires)) { ? ? ? ? ? ? ? ? ? ?setExclusiveOwnerThread(current); ? ? ? ? ? ? ? ? ? ?return true; ? ? ? ? ? ? ? } ? ? ? ? ? } ? ? ? ? ? ?// 當(dāng)前線程就是持有鎖的線程,表明鎖重入 ? ? ? ? ? ?else if (current == getExclusiveOwnerThread()) { ? ? ? ? ? ? ? ?// 利用state整形變量進(jìn)行次數(shù)記錄 ? ? ? ? ? ? ? ?int nextc = c + acquires; ? ? ? ? ? ? ? ?// 如果超過了int表示范圍,表明符號(hào)溢出,所以拋出異常0111 1111 + 1 = 1000 0000 ? ? ? ? ? ? ? ?if (nextc < 0) ? ? ? ? ? ? ? ? ? ?throw new Error("Maximum lock count exceeded"); ? ? ? ? ? ? ? ?setState(nextc); ? ? ? ? ? ? ? ?return true; ? ? ? ? ? } ? ? ? ? ? ?// 返回false 表明需要AQS來將當(dāng)前線程放入阻塞隊(duì)列,然后進(jìn)行阻塞操作等待喚醒獲取鎖 ? ? ? ? ? ?return false; ? ? ? } ? ? ? ?// 公平鎖和非公平鎖公用方法,因?yàn)樵卺尫沛i的時(shí)候,并不區(qū)分是否公平 ? ? ? ?protected final boolean tryRelease(int releases) { ? ? ? ? ? ?int c = getState() - releases; ? ? ? ? ? ?// 如果當(dāng)前線程不是上鎖的那個(gè)線程 ? ? ? ? ? ?if (Thread.currentThread() != getExclusiveOwnerThread()) ? ? ? ? ? ? ? ?throw new IllegalMonitorStateException(); ? ? ? ? ? ?boolean free = false; ? ? ? ? ? ?// 不是重入鎖,那么當(dāng)前線程一定是釋放鎖了,然后我們把當(dāng)前AQS用于保存當(dāng)前鎖對(duì)象的變量ExclusiveOwnerThread設(shè)置為null,表明釋放鎖成功 ? ? ? ? ? ?if (c == 0) { ? ? ? ? ? ? ? ?free = true; ? ? ? ? ? ? ? ?setExclusiveOwnerThread(null); ? ? ? ? ? } ? ? ? ? ? ?// 注意:此時(shí)state全局變量沒有改變,也就意味著在setState之前,沒有別的線程能夠獲取鎖,這時(shí)保證了以上的操作原子性 ? ? ? ? ? ?setState(c); ? ? ? ? ? ?// 告訴AQS,我當(dāng)前釋放鎖成功了,你可以去喚醒正在等待鎖的線程了 ? ? ? ? ? ?return free; ? ? ? } ? ? ? ? ?protected final boolean isHeldExclusively() { ? ? ? ? ? ?return getExclusiveOwnerThread() == Thread.currentThread(); ? ? ? } ? ? ? ? ?final ConditionObject newCondition() { ? ? ? ? ? ?return new ConditionObject(); ? ? ? } ? ? } ? ? ?static final class NonfairSync extends Sync { ? ? ? ?// 由ReentrantLock調(diào)用獲取鎖 ? ? ? ?final void lock() { ? ? ? ? ? ?// 非公平鎖,直接搶鎖,不管有沒有線程排隊(duì) ? ? ? ? ? ?if (compareAndSetState(0, 1)) ? ? ? ? ? ? ? ?// 上鎖成功,那么標(biāo)識(shí)當(dāng)前線程為獲取鎖的線程 ? ? ? ? ? ? ? ?setExclusiveOwnerThread(Thread.currentThread()); ? ? ? ? ? ?else ? ? ? ? ? ? ? ?// 搶鎖失敗,進(jìn)入AQS的標(biāo)準(zhǔn)獲取鎖流程 ? ? ? ? ? ? ? ?acquire(1); ? ? ? } ? ? ? ? ?protected final boolean tryAcquire(int acquires) { ? ? ? ? ? ?// 使用父類提供的獲取非公平鎖的方法來獲取鎖 ? ? ? ? ? ?return nonfairTryAcquire(acquires); ? ? ? } ? }? ? ?static final class FairSync extends Sync { ? ? ? ?// 由ReentrantLock調(diào)用 ? ? ? ?final void lock() { ? ? ? ? ? ?// 沒有嘗試搶鎖,直接進(jìn)入AQS標(biāo)準(zhǔn)獲取鎖流程 ? ? ? ? ? ?acquire(1); ? ? ? } ? ? ? ?// AQS調(diào)用,子類自己實(shí)現(xiàn)獲取鎖的流程 ? ? ? ?protected final boolean tryAcquire(int acquires) { ? ? ? ? ? ?final Thread current = Thread.currentThread(); ? ? ? ? ? ?int c = getState(); ? ? ? ? ? ?// 此時(shí)有可能正好獲取鎖的線程釋放了鎖,也有可能本身就沒有線程獲取鎖 ? ? ? ? ? ?if (c == 0) { ? ? ? ? ? ? ? ?// 注意:這里和非公平鎖的區(qū)別在于:hasQueuedPredecessors看看隊(duì)列中是否有線程正在排隊(duì),沒有的話再通過CAS搶鎖 ? ? ? ? ? ? ? ?if (!hasQueuedPredecessors() && ? ? ? ? ? ? ? ? ? ?compareAndSetState(0, acquires)) { ? ? ? ? ? ? ? ? ? ?// 搶鎖成功 ? ? ? ? ? ? ? ? ? ?setExclusiveOwnerThread(current); ? ? ? ? ? ? ? ? ? ?return true; ? ? ? ? ? ? ? } ? ? ? ? ? } ? ? ? ? ? ?// 當(dāng)前線程就是獲取鎖的線程,那么這里是鎖重入,和非公平鎖操作一模一樣 ? ? ? ? ? ?else if (current == getExclusiveOwnerThread()) { ? ? ? ? ? ? ? ?int nextc = c + acquires; ? ? ? ? ? ? ? ?if (nextc < 0) ? ? ? ? ? ? ? ? ? ?throw new Error("Maximum lock count exceeded"); ? ? ? ? ? ? ? ?setState(nextc); ? ? ? ? ? ? ? ?return true; ? ? ? ? ? } ? ? ? ? ? ?// 返回false 表明需要AQS來將當(dāng)前線程放入阻塞隊(duì)列,然后進(jìn)行阻塞操作等待喚醒獲取鎖 ? ? ? ? ? ?return false; ? ? ? } ? } }
核心方法
獲取鎖操作:
public void lock() { ? ?// 直接通過sync同步器上鎖 ? ?sync.lock(); }
釋放鎖操作:
public void unlock() { ? ?sync.release(1); }
ReentrantReadWriteLock 原理
用例
將原來的鎖,分割為兩把鎖:讀鎖、寫鎖。適用于讀多寫少的場(chǎng)景,讀鎖可以并發(fā),寫鎖與其他鎖互斥。寫寫互斥、寫讀互斥、讀讀兼容。
public class ThreadDemo { ? ?static volatile int a;? ? ?public static void readA() { ? ? ? ?System.out.println(a); ? }? ? ?public static void writeA() { ? ? ? ?a++; ? } ? ?public static void main(String[] args) { ? ? ? ?ReentrantReadWriteLock reentrantReadWriteLock = new ReentrantReadWriteLock(); ? ? ? ?ReentrantReadWriteLock.ReadLock readLock = reentrantReadWriteLock.readLock(); ? ? ? ?ReentrantReadWriteLock.WriteLock writeLock = reentrantReadWriteLock.writeLock(); ? ? ? ?Thread readThread1 = new Thread(() -> { ? ? ? ? ? ?readLock.lock(); ? ? ? ? ? ?try { ? ? ? ? ? ? ? ?readA(); ? ? ? ? ? } finally { ? ? ? ? ? ? ? ?readLock.unlock(); ? ? ? ? ? } ? ? ? ? }); ? ? ? ?Thread readThread2 = new Thread(() -> { ? ? ? ? ? ?readLock.lock(); ? ? ? ? ? ?try { ? ? ? ? ? ? ? ?readA(); ? ? ? ? ? } finally { ? ? ? ? ? ? ? ?readLock.unlock(); ? ? ? ? ? } ? ? ? }); ? ? ? ? ?Thread writeThread = new Thread(() -> { ? ? ? ? ? ?writeLock.lock(); ? ? ? ? ? ?try { ? ? ? ? ? ? ? ?writeA(); ? ? ? ? ? } finally { ? ? ? ? ? ? ? ?writeLock.unlock(); ? ? ? ? ? } ? ? ? }); ? ? ? ? ?readThread1.start(); ? ? ? ?readThread2.start(); ? ? ? ?writeThread.start(); ? } }
核心變量和構(gòu)造器
該接口用于獲取讀鎖和寫鎖對(duì)象
public interface ReadWriteLock { ? ?// 用于獲取讀鎖 ? ?Lock readLock(); ? ?// 用于獲取寫鎖 ? ?Lock writeLock(); }
readerLock和writerLock變量用于支撐以上描述的ReadWriteLock接口的讀鎖和寫鎖方法。通過構(gòu)造方法得知,讀寫鎖對(duì)象的創(chuàng)建和用例均依賴于公平鎖或者非公平鎖同步器。
public class ReentrantReadWriteLock implements ReadWriteLock { ? ?// 讀鎖對(duì)象 ? ?private final ReentrantReadWriteLock.ReadLock readerLock; ? ?// 寫鎖對(duì)象 ? ?private final ReentrantReadWriteLock.WriteLock writerLock; ? ?// 同步器 ? ?final Sync sync; ? ?// 默認(rèn)構(gòu)造器,創(chuàng)建了非公平鎖 ? ?public ReentrantReadWriteLock() { ? ? ? ?this(false); ? } ? ?// 根據(jù)fair變量,來選擇創(chuàng)建不同的鎖:公平鎖 FairSync 和非公平鎖 NonfairSync ? ?public ReentrantReadWriteLock(boolean fair) { ? ? ? ?sync = fair ? new FairSync() : new NonfairSync(); ? ? ? ?// 用同步器來創(chuàng)建讀寫鎖對(duì)象 ? ? ? ?readerLock = new ReadLock(this); ? ? ? ?writerLock = new WriteLock(this); ? } ? ?public ReentrantReadWriteLock.WriteLock writeLock() { return writerLock; } ? ?public ReentrantReadWriteLock.ReadLock ?readLock() { return readerLock; } ? ? }
Sync類
核心變量和構(gòu)造器
我們說讀鎖可以多個(gè)線程同時(shí)持有,而寫鎖只允許一個(gè)線程持有,此時(shí)我們稱 讀鎖-----共享鎖 寫鎖------互斥鎖(排他鎖)。然后我們?cè)贏QS中了解到一個(gè)變量state,它是32位的值,那么我們這里將其切割為高16位和低16位。
abstract static class Sync extends AbstractQueuedSynchronizer { ? ?// 高16位用于表示讀鎖 ? ?static final int SHARED_SHIFT ? = 16; ? ?// 用于對(duì)高16位操作:加1 減1 ? ?static final int SHARED_UNIT ? ?= (1 << SHARED_SHIFT); ? ?// 最大讀鎖量 ? ?static final int MAX_COUNT ? ? ?= (1 << SHARED_SHIFT) - 1; ? ?// 用于獲取低16位的值。例如 獲取低八位:0000 0000 1111 1111 ? ?static final int EXCLUSIVE_MASK = (1 << SHARED_SHIFT) - 1; ? ? ?/** 獲取當(dāng)前持有讀鎖的線程數(shù)量 */ ? ?static int sharedCount(int c) ? { return c >>> SHARED_SHIFT; } ? ?/** 獲取當(dāng)前持有寫鎖的線程數(shù)量 */ ? ?static int exclusiveCount(int c) { return c & EXCLUSIVE_MASK; } ? ? ?// 高16位為所有讀鎖獲取,那么我想知道每個(gè)線程對(duì)于讀鎖重入的次數(shù)?采用ThreadLocal來進(jìn)行統(tǒng)計(jì),每個(gè)線程自己統(tǒng)計(jì)自己的 ? ?static final class HoldCounter { ? ? ? ?int count = 0; ? ? ? ?final long tid = getThreadId(Thread.currentThread()); ? } ? ?// 繼承自ThreadLocal,重寫了其中的initialValue方法,該方法將在線程第一次獲取該變量時(shí)調(diào)用初始化HoldCounter計(jì)數(shù)器 ? ?static final class ThreadLocalHoldCounter ? ? ? ?extends ThreadLocal<HoldCounter> { ? ? ? ?public HoldCounter initialValue() { ? ? ? ? ? ?return new HoldCounter(); ? ? ? } ? } ? ?// 創(chuàng)建ThreadLocal對(duì)象 ? ?private transient ThreadLocalHoldCounter readHolds; ? ?// 緩存最后一個(gè)線程獲取的讀鎖數(shù)量 ? ?private transient HoldCounter cachedHoldCounter; ? ?// 保存獲取到該鎖的第一個(gè)讀鎖線程 ? ?private transient Thread firstReader = null; ? ?// 保存第一個(gè)該鎖的第一個(gè)讀鎖線程獲取到的讀鎖數(shù)量 ? ?private transient int firstReaderHoldCount; ? ? ?Sync() { ? ? ? ?// 構(gòu)造器中初始化ThreadLocalHoldCounter ThreadLocal對(duì)象 ? ? ? ?readHolds = new ThreadLocalHoldCounter(); ? ? ? ?// 用于保證可見性,使用了state變量的volatile語(yǔ)義 ? ? ? ?setState(getState()); ? } }
tryAcquire獲取寫鎖的流程
由AQS調(diào)用,用于子類實(shí)現(xiàn)自己的上鎖邏輯,和原有獲取互斥鎖保持一致,
protected final boolean tryAcquire(int acquires) { ? ?// 獲取當(dāng)前線程 ? ?Thread current = Thread.currentThread(); ? ?// 獲取當(dāng)前狀態(tài)值和互斥鎖的數(shù)量 ? ?int c = getState(); ? ?int w = exclusiveCount(c); ? ?// 狀態(tài)值有效 ? ?if (c != 0) { ? ? ? ?// 有線程獲取到了讀鎖或者當(dāng)前線程不是持有互斥鎖的線程 ? ? ? ?if (w == 0 || ?// 有線程獲取到了讀鎖 ? ? ? ? ? ?current != getExclusiveOwnerThread()) // 有線程獲取到了寫鎖 ? ? ? ? ? ?// 返回false 讓AQS執(zhí)行阻塞操作 ? ? ? ? ? ?return false; ? ? ? ?// 寫鎖重入,而又由于寫鎖的數(shù)量保存在低16位,所以直接加就行了 ? ? ? ?if (w + exclusiveCount(acquires) > MAX_COUNT) ? ? ? ? ? ?throw new Error("Maximum lock count exceeded"); ? ? ? ?setState(c + acquires); ? ? ? ?return true; ? } ? ?// 既沒有讀鎖,也沒有寫鎖 ? ?if (writerShouldBlock() || // 由子類實(shí)現(xiàn)判斷當(dāng)前線程是否應(yīng)該獲取寫鎖 ? ? ? ?!compareAndSetState(c, c + acquires)) // 通過CAS搶寫鎖 ? ? ? ?return false; ? ?// 獲取寫鎖成功,那么將當(dāng)前線程標(biāo)識(shí)為獲取互斥鎖的線程對(duì)象 ? ?setExclusiveOwnerThread(current); ? ?return true; }
tryAcquireShared獲取讀鎖的流程獲取寫鎖的流程
protected final int tryAcquireShared(int unused) { ? ?// 獲取到當(dāng)前線程對(duì)象 ? ?Thread current = Thread.currentThread(); ? ?// 獲取到當(dāng)前狀態(tài)值 ? ?int c = getState(); ? ?if (exclusiveCount(c) != 0 && // 有沒有線程持有寫鎖 ? ? ? ?getExclusiveOwnerThread() != current) // 如果有線程獲取到了互斥鎖,那么進(jìn)一步看看是不是當(dāng)前線程 ? ? ? ?// 不是當(dāng)前線程,那么直接返回-1,告訴AQS獲取共享鎖失敗 ? ? ? ?return -1; ? ?// 獲取到讀鎖的持有數(shù)量 ? ?int r = sharedCount(c); ? ?if (!readerShouldBlock() && // 讓子類來判定當(dāng)前獲取讀鎖的線程是否應(yīng)該被阻塞 ? ? ? ?r < MAX_COUNT && // 判斷是否發(fā)生了溢出 ? ? ? ?compareAndSetState(c, c + SHARED_UNIT)) { // 直接CAS 增加state的高16位的讀鎖持有數(shù)量 ? ? ? ?// 增加高16位之前的計(jì)數(shù)為0,此時(shí)表明當(dāng)前線程就是第一個(gè)獲取讀鎖的線程 ? ? ? ?if (r == 0) { ? ? ? ? ? ?// 注意:持有兩個(gè)變量來優(yōu)化threadlocal ? ? ? ? ? ?firstReader = current; ? ? ? ? ? ?firstReaderHoldCount = 1; ? ? ? } else if (firstReader == current) { ? ? ? ? ? ?// 當(dāng)前獲取讀鎖的線程就是一個(gè)線程,那么此時(shí)表明:鎖重入,直接++計(jì)數(shù)位即可 ? ? ? ? ? ?firstReaderHoldCount++; ? ? ? } else { ? ? ? ? ? ?// 當(dāng)前線程不是第一個(gè)讀線程,此時(shí)將其獲取讀鎖的次數(shù)保存在ThreadLocal中 ? ? ? ? ? ?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; ? } ? ?// 有很多同學(xué)走到這里,直接懵逼?不知道這是啥情況?經(jīng)驗(yàn):在看doug lea寫的代碼時(shí),請(qǐng)注意:經(jīng)常做優(yōu)化,就是把一些常見的場(chǎng)景前置,保證性能 ? ?return fullTryAcquireShared(current); }
fullTryAcquireShared完全獲取讀鎖流程
final int fullTryAcquireShared(Thread current) { ? ?HoldCounter rh = null; ? ?for (;;) { ? ? ? ?int c = getState(); ? ? ? ?// 當(dāng)前已經(jīng)有線程獲取到寫鎖且當(dāng)前獲取寫鎖的線程不是,當(dāng)前線程 ? ? ? ?if (exclusiveCount(c) != 0) { ? ? ? ? ? ?if (getExclusiveOwnerThread() != current) ? ? ? ? ? ? ? ?return -1; ? ? ? } else if (readerShouldBlock()) { ? ? ? ? ? ?// 子類判斷當(dāng)前線程應(yīng)該阻塞 ? ? ? ? ? ?if (firstReader == current) { ? ? ? ? ? ? ? ?// 當(dāng)前線程就是第一個(gè)獲取到讀鎖的線程 ? ? ? ? ? } else { ? ? ? ? ? ? ? ?// 獲取到當(dāng)前線程記錄讀鎖重入次數(shù)的HoldCounter對(duì)象 ? ? ? ? ? ? ? ?if (rh == null) { ? ? ? ? ? ? ? ? ? ?rh = cachedHoldCounter; ? ? ? ? ? ? ? ? ? ?if (rh == null || rh.tid != getThreadId(current)) { ? ? ? ? ? ? ? ? ? ? ? ?rh = readHolds.get(); ? ? ? ? ? ? ? ? ? ? ? ?if (rh.count == 0) ? ? ? ? ? ? ? ? ? ? ? ? ? ?readHolds.remove(); ? ? ? ? ? ? ? ? ? } ? ? ? ? ? ? ? } ? ? ? ? ? ? ? ?// 當(dāng)前讀鎖重入次數(shù)為0時(shí),表明沒有獲取讀鎖,此時(shí)返回-1,阻塞當(dāng)前線程 ? ? ? ? ? ? ? ?if (rh.count == 0) ? ? ? ? ? ? ? ? ? ?return -1; ? ? ? ? ? } ? ? ? } ? ? ? ?// 讀鎖獲取次數(shù)溢出 ? ? ? ?if (sharedCount(c) == MAX_COUNT) ? ? ? ? ? ?throw new Error("Maximum lock count exceeded"); ? ? ? ?// CAS增加讀鎖次數(shù) ? ? ? ?if (compareAndSetState(c, c + SHARED_UNIT)) { ? ? ? ? ? ?if (sharedCount(c) == 0) { ? ? ? ? ? ? ? ?firstReader = current; ? ? ? ? ? ? ? ?firstReaderHoldCount = 1; ? ? ? ? ? } else if (firstReader == current) { ? ? ? ? ? ? ? ?firstReaderHoldCount++; ? ? ? ? ? } else { ? ? ? ? ? ? ? ?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; ? ? ? ? ? } ? ? ? ? ? ?return 1; ? ? ? } ? } }
tryRelease釋放寫鎖的流程
protected final boolean tryRelease(int releases) { ? ?// 沒有獲取寫鎖,為啥能釋放寫鎖呢? ? ?if (!isHeldExclusively()) ? ? ? ?throw new IllegalMonitorStateException(); ? ?int nextc = getState() - releases; ? ?// 釋放完畢后,寫鎖狀態(tài)是否為0(鎖重入),因?yàn)榇藭r(shí)計(jì)算的不是當(dāng)前state,是nextc ? ?boolean free = exclusiveCount(nextc) == 0; ? ?// 如果下一個(gè)狀態(tài)值為0,此時(shí)表明當(dāng)前線程完全釋放了鎖,也即鎖重入為0,那么將當(dāng)前線程對(duì)象從OwnerThread中移除 ? ?if (free) ? ? ? ?setExclusiveOwnerThread(null); ? ?// 此時(shí)設(shè)置全局state變量即可 ? ?setState(nextc); ? ?// 如果返回為true,那么由AQS完成后面線程的喚醒 ? ?return free; }
tryReleaseShared釋放讀鎖的流程
釋放時(shí),需要考慮:重入多少次,就釋放多少次??偨Y(jié):先完成自己的釋放,然后再完成共享的高16位的釋放。
protected final boolean tryReleaseShared(int unused) { ? ?Thread current = Thread.currentThread(); ? ?// 當(dāng)前線程是第一個(gè)獲取到讀鎖的線程 ? ?if (firstReader == current) { ? ? ? ?// 當(dāng)前重入次數(shù)為1,代表什么?代表可以直接釋放,如果不是1,那么表明還持有多個(gè)讀鎖,也即重入多次,那么直接-- ? ? ? ?if (firstReaderHoldCount == 1) ? ? ? ? ? ?firstReader = null; ? ? ? ?else ? ? ? ? ? ?firstReaderHoldCount--; ? } else { ? ? ? ?HoldCounter rh = cachedHoldCounter; ? ? ? ?if (rh == null || rh.tid != getThreadId(current)) ? ? ? ? ? ?rh = readHolds.get(); ? ? ? ?int count = rh.count; ? ? ? ?if (count <= 1) { ? ? ? ? ? ?// 當(dāng)前線程已經(jīng)釋放完讀鎖,那么不需要在ThreadLocal里持有HoldCounter對(duì)象 ? ? ? ? ? ?readHolds.remove(); ? ? ? ? ? ?if (count <= 0) ? ? ? ? ? ? ? ?throw unmatchedUnlockException(); ? ? ? } ? ? ? ?--rh.count; ? } ? ?for (;;) { ? ? ? ?// CAS釋放高16位計(jì)數(shù) ? ? ? ?int c = getState(); ? ? ? ?int nextc = c - SHARED_UNIT; ? ? ? ?if (compareAndSetState(c, nextc)) ? ? ? ? ? ?// 釋放完畢后是否為0,為無鎖狀態(tài),此時(shí)需要干啥?由AQS來喚醒阻塞的線程 ? ? ? ? ? ?return nextc == 0; ? } }
readerShouldBlock和writerShouldBlock模板方法公平鎖實(shí)現(xiàn)
判斷條件只有一個(gè):hasQueuedPredecessors()方法,就是看看AQS的阻塞隊(duì)列里是否有其他線程正在等待,如果有排隊(duì)去。
總結(jié):有人在排隊(duì),那么不插隊(duì)。w->r->r->r 此時(shí)來了個(gè)r:w->r->r->r->r, 此時(shí)來了個(gè)w:w->r->r->r->w。
static final class FairSync extends Sync { ? ?final boolean writerShouldBlock() { ? ? ? ?return hasQueuedPredecessors(); ? } ? ?final boolean readerShouldBlock() { ? ? ? ?return hasQueuedPredecessors(); ? } // w->r->r ? r獲取鎖 w->r->r-r }
readerShouldBlock和writerShouldBlock模板方法非公平鎖實(shí)現(xiàn)
寫線程永遠(yuǎn)false,因?yàn)樽x寫鎖本身適用的是讀多寫少,此時(shí)不應(yīng)該 讓寫線程饑餓,而且非公平,寫鎖永遠(yuǎn)不阻塞,讓它搶,不管前面是否有人排隊(duì),先搶了再說。apparentlyFirstQueuedIsExclusive()第一個(gè)排隊(duì)的是不是寫線程。r(10),當(dāng)前線程是第十一個(gè),此時(shí)已經(jīng)有一個(gè)寫線程排隊(duì),r(10)->w,此時(shí)排隊(duì)去。r(10)->w->r。
static final class NonfairSync extends Sync { ? ?final boolean writerShouldBlock() { ? ? ? ?return false; ? } ? ?final boolean readerShouldBlock() { ? ? ? ?return apparentlyFirstQueuedIsExclusive(); ? } // w->r->r ? r獲取鎖 r->r->r }
到此這篇關(guān)于Java中ReentrantLock和ReentrantReadWriteLock的原理的文章就介紹到這了,更多相關(guān)Java ReentrantLock內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- Java常用鎖synchronized和ReentrantLock的區(qū)別
- Java并發(fā)編程之淺談ReentrantLock
- Java如何使用ReentrantLock實(shí)現(xiàn)長(zhǎng)輪詢
- 徹底了解java中ReentrantLock和AQS的源碼
- Java?多線程并發(fā)?ReentrantReadWriteLock詳情
- Java多線程讀寫鎖ReentrantReadWriteLock類詳解
- Java多線程之ReentrantReadWriteLock源碼解析
- Java多線程 ReentrantReadWriteLock原理及實(shí)例詳解
- Java concurrency之共享鎖和ReentrantReadWriteLock_動(dòng)力節(jié)點(diǎn)Java學(xué)院整理
相關(guān)文章
一個(gè)簡(jiǎn)易的Java多頁(yè)面隊(duì)列爬蟲程序
這篇文章主要為大家詳細(xì)介紹了一個(gè)多頁(yè)面的java爬蟲,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-08-08Windows系統(tǒng)編寫bat腳本啟動(dòng)、停止及重啟Java服務(wù)jar包
在bat文件中我們將編寫一些代碼來運(yùn)行Java jar文件,下面這篇文章主要給大家介紹了關(guān)于Windows系統(tǒng)編寫bat腳本啟動(dòng)、停止及重啟Java服務(wù)jar包的相關(guān)資料,需要的朋友可以參考下2023-12-12SpringBoot2使用Jetty容器操作(替換默認(rèn)Tomcat)
這篇文章主要介紹了SpringBoot2使用Jetty容器操作(替換默認(rèn)Tomcat),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2020-10-10Java實(shí)現(xiàn)驗(yàn)證碼驗(yàn)證功能
Java如何實(shí)現(xiàn)驗(yàn)證碼驗(yàn)證功能呢?日常生活中,驗(yàn)證碼隨處可見,他可以在一定程度上保護(hù)賬號(hào)安全,那么他是怎么實(shí)現(xiàn)的呢?今天通過本文給大家實(shí)例詳解,需要的朋友參考下2017-02-02SpringBoot啟動(dòng)報(bào)錯(cuò)屬性循環(huán)依賴報(bào)錯(cuò)問題的解決
這篇文章主要介紹了SpringBoot啟動(dòng)報(bào)錯(cuò)屬性循環(huán)依賴報(bào)錯(cuò)問題的解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-05-05