ReentrantReadWriteLock
概述
先帶着問題去看這個類:
著作權歸https://pdai.tech所有。 鏈接:https://www.pdai.tech/md/java/thread/java-thread-x-lock-ReentrantReadWriteLock.html
爲什麼有了ReentrantLock還需要ReentrantReadWriteLock? -- 有共享鎖
ReentrantReadWriteLock底層實現原理?
ReentrantReadWriteLock底層讀寫狀態如何設計的? 高16位爲讀鎖,低16位爲寫鎖
讀鎖和寫鎖的最大數量是多少? -- 16位
本地線程計數器ThreadLocalHoldCounter是用來做什麼的?
答:是這裏get就是相當於new了一個對象,用來存放holdCounter的localthread對象
緩存計數器HoldCounter是用來做什麼的?
答:是最後一個獲取到讀鎖的線程計數器,每當有新的線程獲取到讀鎖,這個變量都會更新。當最後一個獲取讀鎖的線程重複獲取讀鎖,或者釋放讀鎖,就會直接使用這個變量,速度更快,相當於緩存。
讀鎖的獲取與釋放是怎麼實現的?
讀鎖實際就是計數++,釋放就是計數-- ,唯一可能影響多線程的地方就是在讀鎖++,--之後進行狀態設置的時候,有個cas操作,可能會有死循環進行操作,那麼如果競爭強度很大的時候,可能一直在循環
寫鎖的獲取與釋放是怎麼實現的?
寫鎖的可以多次上鎖,但是如果是不同的線程進行上鎖,那麼其他線程的上鎖操作就會進入AQS同步隊列
寫鎖如果嘗試上鎖失敗就會掛起線程
釋放:
核心是就是判斷是否當前執行的線程持有了鎖對象(AQS),然後如果是,那麼state減去相應的計數即可
RentrantReadWriteLock爲什麼不支持鎖升級?
什麼是鎖的升降級? RentrantReadWriteLock爲什麼不支持鎖升級?
先要理解什麼是鎖升級和降級:
著作權歸https://pdai.tech所有。 鏈接:https://www.pdai.tech/md/java/thread/java-thread-x-lock-ReentrantReadWriteLock.html
鎖降級指的是寫鎖降級成爲讀鎖。如果當前線程擁有寫鎖,然後將其釋放,最後再獲取讀鎖,這種分段完成的過程不能稱之爲鎖降級。鎖降級是指把持住(當前擁有的)寫鎖,再獲取到讀鎖,隨後釋放(先前擁有的)寫鎖的過程。
讀鎖和寫鎖的牽制關係是什麼?
當寫鎖存在的時候,無法獲取讀鎖(除了當前持有寫鎖的當前線程可以再次獲取)
當寫鎖不存在的時候,讀鎖可以多次獲取
- 重入:此鎖允許reader和writer按照
ReentrantLock
的樣式重新獲取讀取鎖或寫入鎖。在寫入線程保持的所有寫入鎖都已經釋放後,才允許重入reader使用讀取鎖。
writer可以獲取讀取鎖,但reader不能獲取寫入鎖。 - 鎖降級:重入還允許從寫入鎖降級爲讀取鎖,實現方式是:先獲取寫入鎖,然後獲取讀取鎖,最後釋放寫入鎖。但是,從讀取鎖升級到寫入鎖是不可能的。
- 鎖獲取的中斷:讀取鎖和寫入鎖都支持鎖獲取期間的中斷。
- Condition 支持:寫入鎖提供了一個
Condition
實現,對於寫入鎖來說,該實現的行爲與ReentrantLock.newCondition()
提供的Condition
實現對ReentrantLock
所做的行爲相同。當然,此Condition
只能用於寫入鎖。
讀取鎖不支持Condition
,readLock().newCondition()
會拋出UnsupportedOperationException
。 - 監測:此類支持一些確定是讀取鎖還是寫入鎖的方法。這些方法設計用於監視系統狀態,而不是同步控制。
對比:
我們發現reentrantReadWriteLock和ReentrantLock類的組成上就差別有讀寫可重入的鎖上多了2個內部類,一個叫readlock一個叫writelock類
這裏讀寫鎖的讀鎖是多線程共享的,即共享鎖。
這裏讀寫鎖的寫鎖是在更改時不允許其他線程操作的,也就是排他鎖。
構造函數
創建函數默認爲不公平鎖方式
public ReentrantReadWriteLock(boolean fair) {
// 構造函數直接初始化讀鎖和寫鎖,fair使用的是多臺,默認是false
// 注意這個sync就是對aqs的實現
sync = fair ? new FairSync() : new NonfairSync();
readerLock = new ReadLock(this);
writerLock = new WriteLock(this);
}
// 讀鎖和寫鎖的初始化就是把當前對象設置sync
protected ReadLock(ReentrantReadWriteLock lock) {
sync = lock.sync;
}
protected WriteLock(ReentrantReadWriteLock lock) {
sync = lock.sync;
}
內部類分析
java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock#lock
獲取讀鎖
public void lock() {
// 這裏的sync對象是非公平鎖 --> 注意是reentrantreadwritelock -》 中的NonfairSync
sync.acquireShared(1);
}
public final void acquireShared(int arg) {
if (tryAcquireShared(arg) < 0)
doAcquireShared(arg);
}
// java.util.concurrent.locks.ReentrantReadWriteLock.Sync#tryAcquireShared
重點函數分析
讀鎖是如何上鎖的,寫鎖又是如何上鎖的,兩者有什麼差別?
firstReader的目的是什麼,性能如何提升?
爲什麼讀鎖可以共享,寫鎖不可以?
讀鎖和寫鎖兩個如何合作,會不會有衝突?
什麼是鎖降級?
tryAcquireShared讀鎖
java.util.concurrent.locks.ReentrantReadWriteLock.Sync#tryAcquireShared
/**
* 實現父類接口,嘗試獲取共享鎖
* 返回如果計數成功,那麼就返回1
*
* @param unused
* @return
*/
protected final int tryAcquireShared(int unused) {
/*
* Walkthrough:
*
* 1. 如果鎖被其他線程持有,則失敗
* 2. 否則,這個線程有資格lock這個wrt的狀態,因此ask根據隊列策略是否需要阻塞
* 如果不需要,嘗試通過cas修改狀態和更新count來進行授權。
* 注意,不檢查是否可重入的狀態哪一步, 它被推遲到完整版,以避免必須檢查保留計數 更典型的非重入情況。
* 3. 如果步驟2失敗,因爲線程顯然不合格或CAS失敗或計數飽和導致的,則鏈到版本與完全重試循環。
*/
Thread current = Thread.currentThread();
// 獲取狀態
int c = getState();
// 判斷寫鎖的計數不爲0,並且當前持有線程不是自己,那麼獲取讀鎖直接失敗
// exclusiveCount 獲取低16位數據,如果不爲0,說明有寫獨佔線程
// sharedCount 計算寫線程數
// getExclusiveOwnerThread 當前獨佔的線程不是自己
if (exclusiveCount(c) != 0 &&
getExclusiveOwnerThread() != current)
return -1;
// 統計當前讀鎖個數
// sharedCount 計算寫線程數
int r = sharedCount(c);
/**
* 調用當前類中的nonfairsync函數中的數據
* {@link NonfairSync#readerShouldBlock} 可以獲取讀鎖
* 並且讀鎖數量沒有達到上限
* compareAndSetState(c, c + SHARED_UNIT) 高位16 + 1 cas操作 --> 讀鎖(共享鎖)個數加1
*/
if (!readerShouldBlock() &&
r < MAX_COUNT &&
// cas判斷能否搶佔成功
compareAndSetState(c, c + SHARED_UNIT)) {
// r 當前共享單元個數
if (r == 0) {
// 設置第一個讀鎖,設置第一個的目的是爲了性能提升?
// ans: firstReader是獲取讀鎖的第一個線程。如果只有一個線程獲取讀鎖,很明顯,使用這樣一個變量速度更快
firstReader = current;
// 並統計當前讀鎖所在的線程累加的次數
firstReaderHoldCount = 1;
} else if (firstReader == current) {
// 如果一直是當前線程進行獲取讀鎖,那麼計數就一直累加
firstReaderHoldCount++;
} else {
/**
* cachedHoldCounter 代表的是最後一個獲取讀鎖的線程的計數器。
* 這裏get就是相當於new了一個對象
* cachedHoldCounter是最後一個獲取到讀鎖的線程計數器,每當有新的線程獲取到讀鎖,這個變量都會更新。
* 這個變量的目的是:當最後一個獲取讀鎖的線程重複獲取讀鎖,或者釋放讀鎖,就會直接使用這個變量,速度更快,相當於緩存。
* {@link ReentrantReadWriteLock.Sync.ThreadLocalHoldCounter}
* {@link HoldCounter}
*/
HoldCounter rh = cachedHoldCounter;
// 如果最後一個線程計數器是 null 或者不是當前線程,那麼就新建一個 HoldCounter 對象
if (rh == null || rh.tid != getThreadId(current)) {
// 給當前線程新建一個 HoldCounter
// readHolds 實際是一個threadlocal -- 這裏主要是更新了緩存
cachedHoldCounter = rh = readHolds.get();
} else if (rh.count == 0) {
// 如果不是 null,且 count 是 0,就將上個線程的 HoldCounter 覆蓋本地的。
readHolds.set(rh);
}
// 對 count 加一
rh.count++;
}
/**
* add by xiaof 2022年7月4日09:10:14 如果不考慮緩存,是否可以這樣寫
* 後續所有用到的邏輯都要統一一下,這裏主要的目的就是計數,並且前面有做cas操作
* {@link ReentrantReadWriteLock.Sync#readCount}
*/
// readCount++;
return 1;
}
// 如果cas搶佔不成功/線程滿了, 死循環獲取讀鎖。包含鎖降級策略。
return fullTryAcquireShared(current);
}
tryAcquire寫鎖獲取
java.util.concurrent.locks.ReentrantReadWriteLock.Sync#tryAcquire
protected final boolean tryAcquire(int acquires) {
Thread current = Thread.currentThread();
// 獲取計數狀態
int c = getState();
// 獲取寫鎖數量,也就是低16位的個數
int w = exclusiveCount(c);
// 判斷鎖計數不爲0
if (c != 0) {
// (Note: if c != 0 and w == 0 then shared count != 0)
/**
* 只要狀態不爲0,那麼在上鎖的時候就會調用 acquireQueued(addWaiter(Node.EXCLUSIVE), arg) 方法
* {@link AbstractQueuedSynchronizer#acquireQueued(java.util.concurrent.locks.AbstractQueuedSynchronizer.Node, int)}
*/
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;
}
/**
* {@link NonfairSync#writerShouldBlock()}
* 如果計數爲0,第一個函數返回永遠爲 false
* 第二個是cas操作,吧計數++
*/
if (writerShouldBlock() ||
!compareAndSetState(c, c + acquires))
return false;
// 設置鎖的持有對象爲當前線程
setExclusiveOwnerThread(current);
return true;
}
fullTryAcquireShared 獲取讀鎖失敗重試
當存在2個線程競爭獲取寫鎖,然後有一個已經獲取到讀鎖,另外一個讀鎖在同步隊列中的時候觸發
/**
* Full version of acquire for reads, that handles CAS misses
* and reentrant reads not dealt with in tryAcquireShared.
*
* reads線程滿了,或者cas無法設置成功,如果可重入鎖沒有再tryacquire中處理的情況
*/
final int fullTryAcquireShared(Thread current) {
/**
* {@link Sync#tryAcquireShared(int)}
* 這裏部分代碼和tryAcquireShared中的人方法有冗餘,但是整體上更簡單(說白了就是一樣的代碼)
*/
HoldCounter rh = null;
// 這裏是一個自旋重試
for (;;) {
// 獲取讀寫鎖狀態數據
int c = getState();
// exclusiveCount 獲取寫鎖個數
if (exclusiveCount(c) != 0) {
if (getExclusiveOwnerThread() != current)
return -1;
// else we hold the exclusive lock; blocking here
// would cause deadlock.
} else if (readerShouldBlock()) {
// Make sure we're not acquiring read lock reentrantly
// 寫鎖空閒 且 公平策略決定 線程應當被阻塞
// 下面的處理是說,如果是已獲取讀鎖的線程重入讀鎖時,
// 即使公平策略指示應當阻塞也不會阻塞。
// 否則,這也會導致死鎖的。
if (firstReader == current) {
// assert firstReaderHoldCount > 0;
} else { // 當前線程不爲第一個讀線程,上面那個判斷
if (rh == null) {
rh = cachedHoldCounter;
// 計數器爲空或者計數器的tid不爲當前正在運行的線程的tid
if (rh == null || rh.tid != getThreadId(current)) {
rh = readHolds.get();
if (rh.count == 0)
readHolds.remove();
}
}
if (rh.count == 0)
return -1;
}
}
// 讀鎖數量達到最多
if (sharedCount(c) == MAX_COUNT)
throw new Error("Maximum lock count exceeded");
if (compareAndSetState(c, c + SHARED_UNIT)) {
// 申請讀鎖成功,下面的處理跟tryAcquireShared是類似的。
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; // cache for release
}
return 1;
}
}
}
實戰樣例
package com.juc.y2022.aqs.ReentrantReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
/**
* 功能描述
*
* @since 2022-06-29
*/
public class Code01_ReadLockMAX {
public static void main(String[] args) {
// 讀鎖是什麼,如何上鎖的?
// 爲什麼說可以共享?如何實現共享
ReentrantReadWriteLock reentrantReadWriteLock = new ReentrantReadWriteLock();
int count = 0;
// 最大上線 read 第65534次上鎖
// 第65535次就會報錯
int MAX = (1 << 16);
int BREAK = 65535;
for (int i = 0; i < MAX; i++) {
if (i == BREAK) {
System.out.println("break point");
}
System.out.println("read 第" + count++ + "次上鎖");
reentrantReadWriteLock.readLock().lock();
}
System.out.println(count);
}
}
package com.juc.y2022.aqs.ReentrantReadWriteLock;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.stream.IntStream;
/**
* 功能描述
*
* @since 2022-06-29
*/
public class Code02_ReadLockOtherGetReadLock {
public static void main(String[] args) {
// 讀鎖是什麼,如何上鎖的?
// 爲什麼說可以共享?如何實現共享
ReentrantReadWriteLock reentrantReadWriteLock = new ReentrantReadWriteLock();
AtomicInteger count = new AtomicInteger();
int MAX = (1 << 16);
int BREAK = 65535;
// 最大上線 read 第65534次上鎖
// 第65535次就會報錯
IntStream.range(0, MAX - 1).forEach(x -> new Thread(() -> {
System.out.println(Thread.currentThread().getName() + ":read 第" + count.getAndIncrement() + "次上鎖");
if (count.get() == BREAK) {
System.out.println("break point");
}
reentrantReadWriteLock.readLock().lock();
}).start());
System.out.println(count.get());
}
}
package com.juc.y2022.aqs.ReentrantReadWriteLock;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.stream.IntStream;
/**
* 功能描述
*
* @since 2022-07-11
*/
public class Code03_ReadLockLockAndUnlock {
public static void main(String[] args) {
// 讀鎖是什麼,如何上鎖的?
// 爲什麼說可以共享?如何實現共享
ReentrantReadWriteLock reentrantReadWriteLock = new ReentrantReadWriteLock();
ReentrantReadWriteLock.ReadLock readLock = reentrantReadWriteLock.readLock();
AtomicInteger count = new AtomicInteger();
int MAX = (1 << 16);
int BREAK = 65535;
// 最大上線 read 第65534次上鎖
// 第65535次就會報錯
System.out.println(Thread.currentThread().getName() + ":read 第" + count.getAndIncrement() + "次上鎖");
if (count.get() == BREAK) {
System.out.println("break point");
}
readLock.lock();
readLock.lock();
// 解鎖
System.out.println(Thread.currentThread().getName() + ":read 第" + count.get() + "次解鎖");
readLock.unlock();
readLock.lock();
IntStream.range(0, MAX - 1).forEach(x -> new Thread(() -> {
System.out.println(Thread.currentThread().getName() + ":read 第" + count.getAndIncrement() + "次上鎖");
if (count.get() == BREAK) {
System.out.println("break point");
}
readLock.lock();
// 解鎖
System.out.println(Thread.currentThread().getName() + ":read 第" + count.get() + "次解鎖");
readLock.unlock();
}).start());
System.out.println(count.get());
}
}
package com.juc.y2022.aqs.ReentrantReadWriteLock;
import java.util.Scanner;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.stream.IntStream;
/**
* 功能描述
*
* @since 2022-07-11
*/
public class Code04_WriteLockLockAndUnlock {
static AtomicInteger count = new AtomicInteger();
static int MAX = (1 << 16);
static int BREAK = 65535;
public static void main(String[] args) {
// 讀鎖是什麼,如何上鎖的?
// 爲什麼說可以共享?如何實現共享
ReentrantReadWriteLock reentrantReadWriteLock = new ReentrantReadWriteLock();
ReentrantReadWriteLock.WriteLock writeLock = reentrantReadWriteLock.writeLock();
// lockStateNotZeroAndLock(reentrantReadWriteLock);
// writeLockAndUnlock(reentrantReadWriteLock);
// otherThreadWriteLock(reentrantReadWriteLock);
otherThreadWriteLockAndUnlock(reentrantReadWriteLock);
// 最大上線 read 第65534次上鎖
// 第65535次就會報錯
// IntStream.range(0, MAX - 1).forEach(x -> new Thread(() -> {
// System.out.println(Thread.currentThread().getName() + ":read 第" + count.getAndIncrement() + "次上鎖");
// if (count.get() == BREAK) {
// System.out.println("break point");
// }
// writeLock.lock();
// // 解鎖
// System.out.println(Thread.currentThread().getName() + ":read 第" + count.get() + "次解鎖");
// writeLock.unlock();
// }).start());
// System.out.println(count.get());
}
public static void lockStateNotZeroAndLock(ReentrantReadWriteLock reentrantReadWriteLock) {
ReentrantReadWriteLock.ReadLock readLock = reentrantReadWriteLock.readLock();
ReentrantReadWriteLock.WriteLock writeLock = reentrantReadWriteLock.writeLock();
System.out.println(Thread.currentThread().getName() + ":read 第" + count.getAndIncrement() + "次上鎖");
readLock.lock();
readLock.lock();
writeLock.lock();
// 解鎖
System.out.println(Thread.currentThread().getName() + ":read 第" + count.get() + "次解鎖");
writeLock.unlock();
writeLock.lock();
}
public static void writeLockAndUnlock(ReentrantReadWriteLock reentrantReadWriteLock) {
ReentrantReadWriteLock.ReadLock readLock = reentrantReadWriteLock.readLock();
ReentrantReadWriteLock.WriteLock writeLock = reentrantReadWriteLock.writeLock();
System.out.println(Thread.currentThread().getName() + ":read 第" + count.getAndIncrement() + "次上鎖");
writeLock.lock();
// 解鎖
System.out.println(Thread.currentThread().getName() + ":read 第" + count.get() + "次解鎖");
writeLock.unlock();
}
public static void otherThreadWriteLock(ReentrantReadWriteLock reentrantReadWriteLock) {
ReentrantReadWriteLock.WriteLock writeLock = reentrantReadWriteLock.writeLock();
System.out.println(Thread.currentThread().getName() + ":read 第" + count.getAndIncrement() + "次上鎖");
writeLock.lock();
writeLock.lock();
// 另外一個線程進行上鎖
IntStream.range(0, 2).forEach(x -> new Thread(() -> {
System.out.println(Thread.currentThread().getName() + ":read 第" + count.getAndIncrement() + "次上鎖");
writeLock.lock();
}).start());
System.out.println(count.get());
}
public static void otherThreadWriteLockAndUnlock(ReentrantReadWriteLock reentrantReadWriteLock) {
ReentrantReadWriteLock.WriteLock writeLock = reentrantReadWriteLock.writeLock();
// 另外一個線程進行上鎖
IntStream.range(0, 2).forEach(x -> new Thread(() -> {
while (true) {
System.out.println(Thread.currentThread().getName() + ":writeLock 第" + count.getAndIncrement() + "次上鎖");
writeLock.lock();
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
int flag = 0;
IntStream.range(0, flag).forEach(xx -> {
System.out.println(Thread.currentThread().getName() + ":writeLock 第" + count.getAndDecrement() + "次解鎖:" + xx);
writeLock.unlock();
});
}
}).start());
}
static int getChoiceWithTimeout(int timeout) {
Callable<Integer> k = () -> {
return new Scanner(System.in).nextInt();
};
Long start = System.currentTimeMillis();
int choice = 0;
boolean valid;
ExecutorService l = Executors.newFixedThreadPool(1);
Future<Integer> g;
g = l.submit(k);
done: while (System.currentTimeMillis() - start < timeout) {
do {
valid = true;
if (g.isDone()) {
try {
choice = g.get();
break done;
} catch (InterruptedException | ExecutionException | IllegalArgumentException e) {
e.printStackTrace();
}
}
} while (!valid);
}
g.cancel(true);
return choice;
}
}
package com.juc.y2022.aqs.ReentrantReadWriteLock;
import java.util.Scanner;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.stream.IntStream;
/**
* 功能描述
*
* @since 2022-07-11
*/
public class Code05_WRGetLock {
static AtomicInteger count = new AtomicInteger();
static int MAX = (1 << 16);
static int BREAK = 65535;
public static void main(String[] args) {
// 讀鎖是什麼,如何上鎖的?
// 爲什麼說可以共享?如何實現共享
ReentrantReadWriteLock reentrantReadWriteLock = new ReentrantReadWriteLock();
// wrgetlockWait(reentrantReadWriteLock);
readgetlockWait(reentrantReadWriteLock);
}
public static void wrgetlockWait(ReentrantReadWriteLock reentrantReadWriteLock) {
ReentrantReadWriteLock.ReadLock readLock = reentrantReadWriteLock.readLock();
ReentrantReadWriteLock.WriteLock writeLock = reentrantReadWriteLock.writeLock();
writeLock.lock();
// **當寫鎖存在的時候,無法獲取讀鎖(除了當前持有寫鎖的當前線程可以再次獲取)**
IntStream.range(0, 2).forEach(x -> new Thread(() -> {
try {
System.out.println(Thread.currentThread() + ":在寫鎖存在的情況下獲取讀鎖");
if (readLock.tryLock(4, TimeUnit.SECONDS)) {
System.out.println(Thread.currentThread() + ":在寫鎖存在的情況下獲取讀鎖" + ":success");
} else {
System.out.println(Thread.currentThread() + ":在寫鎖存在的情況下獲取讀鎖" + ":fail");
}
} catch (Exception e) {
e.printStackTrace();
}
}).start());
getChoiceWithTimeout(1000 * 100);
}
public static void readgetlockWait(ReentrantReadWriteLock reentrantReadWriteLock) {
ReentrantReadWriteLock.ReadLock readLock = reentrantReadWriteLock.readLock();
ReentrantReadWriteLock.WriteLock writeLock = reentrantReadWriteLock.writeLock();
// **當寫鎖存在的時候,無法獲取讀鎖(除了當前持有寫鎖的當前線程可以再次獲取)**
IntStream.range(0, 2).forEach(x -> new Thread(() -> {
try {
System.out.println(Thread.currentThread() + ":在寫鎖存在的情況下獲取讀鎖");
if (readLock.tryLock(4, TimeUnit.SECONDS)) {
System.out.println(Thread.currentThread() + ":在寫鎖存在的情況下獲取讀鎖" + ":success");
} else {
System.out.println(Thread.currentThread() + ":在寫鎖存在的情況下獲取讀鎖" + ":fail");
}
} catch (Exception e) {
e.printStackTrace();
}
}).start());
}
static int getChoiceWithTimeout(int timeout) {
Callable<Integer> k = () -> {
return new Scanner(System.in).nextInt();
};
Long start = System.currentTimeMillis();
int choice = 0;
boolean valid;
ExecutorService l = Executors.newFixedThreadPool(1);
Future<Integer> g;
g = l.submit(k);
done: while (System.currentTimeMillis() - start < timeout) {
do {
valid = true;
if (g.isDone()) {
try {
choice = g.get();
break done;
} catch (InterruptedException | ExecutionException | IllegalArgumentException e) {
e.printStackTrace();
}
}
} while (!valid);
}
g.cancel(true);
return choice;
}
}
package com.juc.y2022.aqs.ReentrantReadWriteLock;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.stream.IntStream;
/**
* 功能描述
* 鎖降級:重入還允許從寫入鎖降級爲讀取鎖,實現方式是:先獲取寫入鎖,然後獲取讀取鎖,最後釋放寫入鎖。但是,**從讀取鎖升級到寫入鎖是不可能的。**
*
* @since 2022-07-11
*/
public class Code06_UpAndDownLock {
static AtomicInteger count = new AtomicInteger();
static int MAX = (1 << 16);
static int BREAK = 65535;
public static void main(String[] args) {
ReentrantReadWriteLock reentrantReadWriteLock = new ReentrantReadWriteLock();
tryDownLock(reentrantReadWriteLock);
}
public static void tryDownLock(ReentrantReadWriteLock reentrantReadWriteLock) {
ReentrantReadWriteLock.ReadLock readLock = reentrantReadWriteLock.readLock();
ReentrantReadWriteLock.WriteLock writeLock = reentrantReadWriteLock.writeLock();
IntStream.range(0, 1).forEach(x -> new Thread(() -> write(readLock, writeLock)).start());
IntStream.range(0, 1).forEach(x -> new Thread(() -> read(readLock, writeLock)).start());
}
public static void read(ReentrantReadWriteLock.ReadLock readLock, ReentrantReadWriteLock.WriteLock writeLock) {
System.out.println(Thread.currentThread() + " begin get read lock");
readLock.lock();
try {
System.out.println(Thread.currentThread() + " get read lock, begin execute");
Thread.sleep(20);
System.out.println(Thread.currentThread() + " try up read lock as write lock");
// 讀鎖升級
writeLock.lock();
System.out.println(Thread.currentThread() + " 讀鎖升級爲寫鎖成功");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
readLock.unlock();
System.out.println(Thread.currentThread() + " 釋放讀鎖");
}
}
public static void write(ReentrantReadWriteLock.ReadLock readLock, ReentrantReadWriteLock.WriteLock writeLock) {
System.out.println(Thread.currentThread() + " begin get 寫 lock");
writeLock.lock();
try {
System.out.println(Thread.currentThread() + " get 寫 lock, begin execute");
Thread.sleep(40);
System.out.println(Thread.currentThread() + " try up read lock as write lock");
// 寫鎖降級讀鎖
readLock.lock();
System.out.println(Thread.currentThread() + " 寫鎖降級爲讀鎖成功");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
System.out.println(Thread.currentThread() + " 釋放寫鎖");
writeLock.unlock();
readLock.unlock();
}
}
}
package com.juc.y2022.aqs.ReentrantReadWriteLock;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.stream.IntStream;
/**
* 功能描述
* 獲取讀鎖失敗,重試
*
* @since 2022-07-11
*/
public class Code07_ReadLockFail {
static AtomicInteger count = new AtomicInteger();
static int MAX = (1 << 16);
static int BREAK = 65535;
public static void main(String[] args) {
ReentrantReadWriteLock reentrantReadWriteLock = new ReentrantReadWriteLock();
// fulltry(reentrantReadWriteLock);
fulltry2(reentrantReadWriteLock);
}
public static void fulltry(ReentrantReadWriteLock reentrantReadWriteLock) {
ReentrantReadWriteLock.ReadLock readLock = reentrantReadWriteLock.readLock();
ReentrantReadWriteLock.WriteLock writeLock = reentrantReadWriteLock.writeLock();
// **當寫鎖存在的時候,無法獲取讀鎖(除了當前持有寫鎖的當前線程可以再次獲取)**
IntStream.range(0, 2).forEach(x -> new Thread(() -> {
try {
System.out.println(Thread.currentThread() + ": 獲取寫鎖");
writeLock.lock();
System.out.println(Thread.currentThread() + ": 獲取寫鎖-success");
System.out.println(Thread.currentThread() + ": 獲取讀鎖");
readLock.lock();
System.out.println(Thread.currentThread() + ": 獲取讀鎖-success");
} catch (Exception e) {
e.printStackTrace();
}
}).start());
}
public static void fulltry2(ReentrantReadWriteLock reentrantReadWriteLock) {
ReentrantReadWriteLock.ReadLock readLock = reentrantReadWriteLock.readLock();
ReentrantReadWriteLock.WriteLock writeLock = reentrantReadWriteLock.writeLock();
// **當寫鎖存在的時候,無法獲取讀鎖(除了當前持有寫鎖的當前線程可以再次獲取)**
IntStream.range(0, 22).forEach(x -> new Thread(() -> {
try {
System.out.println(Thread.currentThread() + ": 獲取讀鎖");
readLock.lock();
System.out.println(Thread.currentThread() + ": 獲取讀鎖-success");
int random1 = (int) (Math.random() * 10);
if (random1 >= 9) {
System.out.println(Thread.currentThread() + ": 獲取寫鎖");
writeLock.lock();
System.out.println(Thread.currentThread() + ": 獲取---------寫鎖-success");
}
} catch (Exception e) {
e.printStackTrace();
}
}).start());
}
}
總結
爲什麼讀鎖是共享鎖:
根本原因是state計數的位置不同,共享鎖是int的高位(前16)計數的,而且不會去判斷是否已經有讀鎖,直接計數++即可
爲什麼寫鎖是排他鎖:
state計數的是低位的16位,並且會判斷是否已經存在持有寫鎖,如果有其他線程持有寫鎖,那麼就會進入同步隊列等待
參考
https://www.pdai.tech/md/java/thread/java-thread-x-lock-ReentrantReadWriteLock.html
https://love1024.blog.csdn.net/article/details/80235197