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)橥ㄟ^大量測試下來,發(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 原理
用例
將原來的鎖,分割為兩把鎖:讀鎖、寫鎖。適用于讀多寫少的場景,讀鎖可以并發(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語義
? ? ? ?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)化,就是把一些常見的場景前置,保證性能
? ?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)長輪詢
- 徹底了解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)文章
Windows系統(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-12
SpringBoot2使用Jetty容器操作(替換默認(rèn)Tomcat)
這篇文章主要介紹了SpringBoot2使用Jetty容器操作(替換默認(rèn)Tomcat),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2020-10-10
Java實(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-02
SpringBoot啟動(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

