ReentrantLock
實現Lock接口的一個類支持重入性,表示能夠對共享資源能夠重複加鎖,及當前線程再次獲取該鎖不會被阻塞
1.鎖的可重入
final boolean nonfairTryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
if (compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
if (nextc < 0) // overflow
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
執行兩種邏輯1.當前鎖未被任何線程佔有 則鎖被當前線程獲取 2.已經被線程佔有判斷是否是當前線程 ,如果是則獲取鎖對象計數+1
釋放鎖
protected final boolean tryRelease(int releases) {
int c = getState() - releases;
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false;
if (c == 0) {
free = true;
setExclusiveOwnerThread(null);
}
setState(c);
return free;
}
``
同步鎖釋放必須等到同步狀態爲0才能成功釋放,當一個鎖獲取一次,計數+1,所以釋放一次計數-1,若返回值同步狀態不爲0則爲false
2.公平鎖與非公平鎖
一個公平鎖獲取順序符合FIFO先到先得
ReentranLock無參構造時非公平鎖
傳入參數true公平鎖false非公平鎖
```javascript
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
公平鎖每次都是從同步隊列第一個節點獲取鎖,而非公平鎖則不一定,所以公平鎖保證請求資源的絕對順序但是性能開銷高於非公平鎖。
ReentrantReadWriteLock
1.讀寫鎖
獨佔鎖一般synchhronized或者concurrents實現Lock接口ReentrantLock實現獨佔鎖,同一時刻只有一個線程能夠獲取鎖
讀寫所允許同一時刻被多個讀線程訪問,但是在寫線程訪問時,所有的讀線程和其他的寫線程都會被阻塞。
2.寫鎖的獲取
同一時刻寫鎖不能被多個線程鎖獲取,寫鎖時獨佔式鎖實現的方式重寫AQS中的tryAcquire方法實現
protected final boolean tryAcquire(int acquires) {
Thread current = Thread.currentThread();
int c = getState(); // 1. 獲取寫鎖當前的同步狀態
int w = exclusiveCount(c); // 2. 獲取寫鎖獲取的次數
if (c != 0) {
// (Note: if c != 0 and w == 0 then shared count != 0)
// 3.1 當讀鎖已被讀線程獲取或者當前線程不是已經獲取寫鎖的線程的話
// 當前線程獲取寫鎖失敗
if (w == 0 || current != getExclusiveOwnerThread())
return false;
if (w + exclusiveCount(acquires) > MAX_COUNT)
throw new Error("Maximum lock count exceeded");
// Reentrant acquire
// 3.2 當前線程獲取寫鎖,支持可重複加鎖
setState(c + acquires); //acquires重複加鎖
return true;
}
// 3.3 寫鎖未被任何線程獲取,當前線程可獲取寫鎖
if (writerShouldBlock() ||
!compareAndSetState(c, c + acquires))
return false;
setExclusiveOwnerThread(current);
return true;
}
3.寫鎖的釋放
重寫AQS的tryRelease方法完成寫鎖的釋放
protected final boolean tryRelease(int releases) {
if (!isHeldExclusively())
throw new IllegalMonitorStateException();
//1. 同步狀態減去寫狀態
int nextc = getState() - releases;
//2. 當前寫狀態是否爲0,爲0則釋放寫鎖
boolean free = exclusiveCount(nextc) == 0;
if (free)
setExclusiveOwnerThread(null);
//3. 不爲0則更新同步狀態
setState(nextc);
return free;
}
同步狀態的情況下進入寫鎖類似於可重入鎖的方式,將寫鎖也是爲重入鎖所以在釋放時,用同步狀態減去寫狀態帶來的Monitor++;
4.讀鎖
讀鎖不是獨佔式鎖,同一時刻多個線程可以同時獲取的一種共享鎖的方式
Thread current = Thread.currentThread();
int c = getState();
//1. 如果寫鎖已經被獲取並且獲取寫鎖的線程不是當前線程的話,當前
// 線程獲取讀鎖失敗返回-1
if (exclusiveCount(c) != 0 &&
getExclusiveOwnerThread() != current)
return -1;
int r = sharedCount(c);
if (!readerShouldBlock() &&
r < MAX_COUNT &&
//2. 當前線程獲取讀鎖
compareAndSetState(c, c + SHARED_UNIT)) {
//3. 下面的代碼主要是新增的一些功能,比如getReadHoldCount()方法
//返回當前獲取讀鎖的次數
if (r == 0) {
firstReader = current;
firstReaderHoldCount = 1;
} else if (firstReader == current) {
firstReaderHoldCount++;
} else {
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;
}
//4. 處理在第二步中CAS操作失敗的自旋已經實現重入性
return fullTryAcquireShared(current);
}
(1). 與寫鎖邏輯相同,對當前鎖的狀態進行胖多,如果寫鎖被獲取,但是寫鎖線程不是當前線程,則失敗不能進入寫鎖,CAS自旋成功進入,更新狀態
(2). 當前線程成功獲取讀鎖,返回讀鎖的次數
5.讀鎖的釋放
protected final boolean tryReleaseShared(int unused) {
Thread current = Thread.currentThread();
if (firstReader == current) {
// assert firstReaderHoldCount > 0;
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) {
readHolds.remove();
if (count <= 0)
throw unmatchedUnlockException();
}
--rh.count;
}
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;
}
}
6.鎖降級
讀寫鎖支持鎖降級,遵循按照獲取寫鎖,獲取讀鎖再釋放寫鎖的次序,寫鎖能夠降級成爲讀鎖。
[資源參考:https://www.jianshu.com/p/27860941a77b?tdsourcetag=s_pcqq_aiomsg 特此聲明]