一,AQS源碼博文:併發編程:AbstractQueuedSynchronizer源碼分析
二,ReentrantReadWriteLock讀寫鎖介紹
1,讀寫鎖介紹
ReentrantReadWriteLock 雖然與 ReentrantLock 沒有直接關係,但是在功能上算是對 ReentrantLock 的擴展。在 ReentrantLock 重入獨佔鎖的功能上,添加了共享鎖的擴展,分別對應 ReentrantReadWriteLock 的寫鎖(WriteLock)和讀鎖(ReadLock)。ReentrantReadWriteLock 內部定義 Sync 類繼承自 AQS 對加鎖方式進行擴展,在讀鎖寫鎖獲取以及讀寫鎖交叉獲取提供了自身機制,以此保證線程同步。ReentrantReadWriteLock 在加鎖過程中,對 Integer 的32位進行分割,以高16位表示共享鎖,以低16位表示獨佔鎖。在進行加鎖時,通過左移右移以及與運算判斷當前加鎖狀態及重入狀態,並進線程進行調度。
2,類圖
* ReentrantReadWriteLock 實現自 ReadWriteLock 接口,並在內部定義了鎖實現了相關內部類。
* Sync,FairSync,NonfairSync 三個內部類是對 AQS 的層次擴展,用來擴展加鎖的一系列邏輯處理
* WriteLock,ReadLock 兩個內部類實現自 Lock 接口,並通過 ReentrantReadWriteLock 創建後對外提供,在應用層進行不同的加鎖處理
3,常用API
// 創建讀寫鎖, 通過參數指定公平鎖/非公平鎖
public ReentrantReadWriteLock();
public ReentrantReadWriteLock(boolean fair);
// 創建讀鎖
public ReentrantReadWriteLock.ReadLock readLock()
// 創建寫鎖
public ReentrantReadWriteLock.WriteLock writeLock();
// 嘗試加鎖
public boolean tryLock();
public boolean tryLock(long timeout, TimeUnit unit);
// 加鎖
public void lock();
public void lockInterruptibly() throws InterruptedException;
// 釋放鎖
public void unlock();
* 在上述API中,關於鎖操作的一些API,都存在讀鎖和寫鎖的區別;其中,在讀鎖和寫鎖的處理中,又存在公平鎖和非公平鎖的區分;
* 後續源碼分析中,讀鎖和寫鎖會分別分析,但是公平鎖和分公平鎖只對非公平鎖進行分析,公平鎖參考重入鎖
4,ReentrantReadWriteLock 加鎖分析
4.1,整體鎖狀態存儲
* ReentrantReadWriteLock 在鎖狀態存儲中,對 Integer 32位進行拆分,使用高16位存儲共享鎖,使用低16位存儲獨佔鎖。所以無論是共享鎖的共享次數還是獨佔鎖的重入次數,最高爲((1 << 16) - 1)。
// 1 << 16就是1在二進制情況下右移16位結果爲
10000 0000 0000 0000 = 2^16 = 65536
// (1 << 16) - 1也是在二進制下進行減操作
1111 1111 1111 1111 = 2^16 - 1 = 65535
* ReentrantReadWriteLock 在獨佔鎖加鎖時,是對低16位進行加1,在獲取獨佔鎖的重入次數時,也是對低16位進行與運算以獲取結果加鎖具體看代碼框
// 獨佔鎖state遞增
compareAndSetState(c, c + 1)
// 獨佔鎖獲取重入次數
// EXCLUSIVE_MASK:常量,最終結果爲 (1111 1111 1111 1111),也就是對應 Integer 的低16位
// 用c和 EXCLUSIVE_MASK 進行與運算,也就是用c的低16位和 EXCLUSIVE_MASK 進行與運算,
// 與運算同1爲1,其餘爲0,所以獲取到的結果就是c的低16位表示的數字,然後再默認轉換10進制,即爲重入數量
static int exclusiveCount(int c) {
return c & EXCLUSIVE_MASK;
}
static final int EXCLUSIVE_MASK = (1 << SHARED_SHIFT) - 1;
static final int SHARED_SHIFT = 16;
// 比如,此時共享爲3次,重入爲0次,則c的二進制表示爲
0000 0000 0000 0011 0000 0000 0000 0000
// EXCLUSIVE_MASK的二進制表示爲
0000 0000 0000 0000 1111 1111 1111 1111
// 運算結果
0000 0000 0000 0011 0000 0000 0000 0000
& 0000 0000 0000 0000 1111 1111 1111 1111
-------------------------------------------
0000 0000 0000 0000 0000 0000 0000 0000 = 0
// 所以此時獲取到的獨佔鎖數量爲0
// 假設可以獲取成功,則對獨佔鎖遞增,通過CAS,用c + 1代替 c,此時,c的二進制表示爲:
0000 0000 0000 0011 0000 0000 0000 0001
// 這時候繼續計算,獲取獨佔鎖的次數,運算如下
0000 0000 0000 0011 0000 0000 0000 0001
& 0000 0000 0000 0000 1111 1111 1111 1111
-------------------------------------------
0000 0000 0000 0000 0000 0000 0000 0001 = 1
* 同樣,ReentrantReadWriteLock 在對共享鎖加鎖時,是對高16位進行加鎖,在獲取共享鎖次數時,先對 state 無符號右移16位,然後直接獲取結果,具體過程如代碼框
// 加鎖成功後,c遞增,按照重入邏輯,遞增應該爲1
// 而此時遞增了(1 << SHARED_SHIFT),也就是二進制下 1 0000 0000 0000 0000
// 除去低16位,也就是剛好在高16位上遞增1
compareAndSetState(c, c + SHARED_UNIT)
static final int SHARED_UNIT = (1 << SHARED_SHIFT);
static final int SHARED_SHIFT = 16;
// 共享鎖獲取鎖重入次數
// 直接將state無符號右移16位,表示的就是共享鎖的重入次數
// 而右移去掉的16位,也就剛好是獨佔鎖表示的低16位
static int sharedCount(int c) {
return c >>> SHARED_SHIFT;
}
static final int SHARED_SHIFT = 16;
// 繼續舉例,比如此時共享次數3,獨佔次數爲3,則state的二進制表示爲
0000 0000 0000 0011 0000 0000 0000 0011
// 無符號右移16位後,高位補0,值如下
0000 0000 0000 0000 0000 0000 0000 0011 = 3
// 假設此時共享鎖獲取成功,state加上(1 << SHARED_SHIFT),如下
0000 0000 0000 0011 0000 0000 0000 0011
+ 0000 0000 0000 0001 0000 0000 0000 0000
-------------------------------------------
0000 0000 0000 0100 0000 0000 0000 0000
// 此時繼續獲取數量,對state無符號右移16位,結果如下
0000 0000 0000 0000 0000 0000 0000 0100 = 4
5,功能DEMO
package com.gupao.concurrent;
import java.util.concurrent.locks.ReentrantReadWriteLock;
/**
* @author pj_zhang
* @create 2019-10-19 12:37
**/
public class ReadAndWriteLockTest {
private static ReentrantReadWriteLock reentrantReadWriteLock = new ReentrantReadWriteLock();
private static ReentrantReadWriteLock.ReadLock readLock = reentrantReadWriteLock.readLock();
private static ReentrantReadWriteLock.WriteLock writeLock = reentrantReadWriteLock.writeLock();
public static void main(String[] args) throws InterruptedException {
testReadAndWriteLock();
}
public static void testWriteAndReadLock() throws InterruptedException {
new Thread(() -> {
System.out.println("開始獲取鎖。。。");
// 先獲取寫鎖後獲取讀鎖
// 讀鎖會順利獲取
writeLock.lock();
System.out.println("獲取寫鎖成功。。。");
readLock.lock();
System.out.println("獲取讀鎖成功。。。");
writeLock.unlock();
System.out.println("釋放讀鎖成功。。。");
readLock.unlock();
System.out.println("釋放寫鎖成功");
}, "READ_AND_WRITE").start();
}
public static void testReadAndWriteLock() throws InterruptedException {
new Thread(() -> {
System.out.println("開始獲取鎖。。。");
// 先獲取讀寫再獲取寫鎖
// 此時已經存在鎖,則獨佔鎖獲取失敗
readLock.lock();
System.out.println("獲取讀鎖成功。。。");
writeLock.lock();
System.out.println("獲取寫鎖成功。。。");
writeLock.unlock();
System.out.println("釋放讀鎖成功。。。");
readLock.unlock();
System.out.println("釋放寫鎖成功");
}, "READ_AND_WRITE").start();
}
/**
* 不同線程測試讀寫鎖
*
* @throws InterruptedException
*/
public static void testLockWithDiffThread() throws InterruptedException {
// 先啓動一個讀鎖
new Thread(() -> {
System.out.println(Thread.currentThread().getName() + "加讀鎖前執行。。。, time: " + System.currentTimeMillis());
readLock.lock();
System.out.println(Thread.currentThread().getName() + "加讀鎖後執行。。。, time: " + System.currentTimeMillis());
sleep(1000);
readLock.unlock();
}, "READ_1").start();
Thread.sleep(10);
// 再一個一個讀鎖,模擬讀鎖不阻塞
new Thread(() -> {
System.out.println(Thread.currentThread().getName() + "加讀鎖前執行。。。, time: " + System.currentTimeMillis());
readLock.lock();
System.out.println(Thread.currentThread().getName() + "加讀鎖後執行。。。, time: " + System.currentTimeMillis());
sleep(1000);
readLock.unlock();
}, "READ_2").start();
Thread.sleep(10);
// 啓動一個寫鎖,模擬寫鎖阻塞
new Thread(() -> {
System.out.println(Thread.currentThread().getName() + "_加寫鎖前執行。。。, time: " + System.currentTimeMillis());
writeLock.lock();
System.out.println(Thread.currentThread().getName() + "_加寫鎖後執行。。。, time: " + System.currentTimeMillis());
sleep(1000);
writeLock.unlock();
}, "WRITE").start();
Thread.sleep(10);
// 自啓動一個讀鎖,模擬共享鎖執行時,阻塞一個寫鎖,之後再獲取讀鎖,在寫鎖後執行
new Thread(() -> {
System.out.println(Thread.currentThread().getName() + "加讀鎖前執行。。。, time: " + System.currentTimeMillis());
readLock.lock();
System.out.println(Thread.currentThread().getName() + "加讀鎖後執行。。。, time: " + System.currentTimeMillis());
sleep(1000);
readLock.unlock();
}, "READ_3").start();
}
private static void sleep(long millSeconds) {
try {
Thread.sleep(millSeconds);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
* testLockWithDiffThread() 執行結果:
1)在不同線程間,讀鎖間共享,不會進行鎖競爭,通過CPU調度依次執行
2)寫鎖排斥,在嘗試獲取寫鎖時,會先判斷是否存在線程持有寫鎖或者讀寫,如果存在,則添加到AQS同步隊列中等待喚醒
3)寫鎖等待後,再啓動一個讀鎖,此時因爲寫鎖等待,並且已經對低16位的獨佔鎖標識進行修改,且鎖線程與當前線程不一致。則當前讀鎖線程獲取鎖失敗,依次進入AQS隊列等待
* testReadAndWriteLock 執行結果
1)在同一線程後,嘗試獲取兩種鎖,從打印結果可以看出,線程獲取讀鎖後,再獲取寫鎖時阻塞,等待讀鎖釋放
2)線程在獲取讀鎖後,對高16進行修改,並設置鎖線程爲當前線程
3)線程繼續獲取寫鎖,因爲高位已經被修改,所以寫鎖獲取失敗,等待讀鎖釋放
* testWriteAndReadLock 執行結果
1)在同一線程中,嘗試獲取兩種所,從打印結果可以看出,線程獲取寫鎖後,再獲取讀鎖成功
2)線程獲取寫鎖後,對低16位進行修改,並設置鎖線程爲當前線程
3)線程繼續獲取讀鎖,雖然判斷獨佔線程已經存在,但是加鎖線程表示當前線程,所以線程獲取鎖成功
三,ReentrantReadWriteLock部分源碼分析
1,初始化部分
* ReentrantReadWriteLock():調用重載構造器,並傳遞參數表示默認非公平鎖
public ReentrantReadWriteLock() {
this(false);
}
* ReentrantReadWriteLock(boolean fair)
public ReentrantReadWriteLock(boolean fair) {
// 通過入參表示公平鎖還是非公平鎖
sync = fair ? new FairSync() : new NonfairSync();
// 初始化讀寫鎖對象
readerLock = new ReadLock(this);
writerLock = new WriteLock(this);
}
* 初始化讀寫鎖
// 初始化讀鎖
protected ReadLock(ReentrantReadWriteLock lock) {
sync = lock.sync;
}
// 初始化寫鎖
protected WriteLock(ReentrantReadWriteLock lock) {
sync = lock.sync;
}
* 獲取讀寫鎖
// 獲取寫鎖
public ReentrantReadWriteLock.WriteLock writeLock() {
return writerLock;
}
// 獲取讀鎖
public ReentrantReadWriteLock.ReadLock readLock() {
return readerLock;
}
四,ReentrantReadWriteLock.ReadLock源碼分析
1,嘗試加鎖
* tryLock():直接通過 Sync 嘗試加讀鎖
public boolean tryLock() {
return sync.tryReadLock();
}
* tryReadLock()
final boolean tryReadLock() {
// 獲取當前線程
Thread current = Thread.currentThread();
// 自旋嘗試獲取鎖,可能存在線程競爭導致的CAS失敗問題
for (;;) {
// 獲取當前的加鎖次數,注意此處次數是經過高低位處理後的次數,並不是真實次數,需要進行解析
int c = getState();
// exclusiveCount:低16位解析,獲取獨佔鎖數量,判斷是否存在獨佔鎖
// getExclusiveOwnerThread:判斷當前線程是否是鎖線程
// 所以如果存在獨佔鎖,並且鎖線程不是當前線程,則嘗試失敗
// 否則當前步驟成功
if (exclusiveCount(c) != 0 &&
getExclusiveOwnerThread() != current)
return false;
// 高16位解析,獲取共享鎖數量
int r = sharedCount(c);
// 共享鎖最大值判斷,
// MAX_COUNT = (1 << SHARED_SHIFT) - 1;
if (r == MAX_COUNT)
throw new Error("Maximum lock count exceeded");
// 按照重入邏輯,如果可以加鎖,則是對state+1
// 但是共享鎖是對高16位進行操作,所以應該在高16位的基礎上加1,
// SHARED_UNIT = (1 << SHARED_SHIFT);
if (compareAndSetState(c, c + SHARED_UNIT)) {
// r爲0,表示當前並沒有共享鎖進入,則初始化當前線程爲頭線程
if (r == 0) {
firstReader = current;
firstReaderHoldCount = 1;
// 當前線程重入,則對重入此時遞增
} else if (firstReader == current) {
firstReaderHoldCount++;
} else {
// 此處表示獲取共享鎖的線程不是當前鎖線程
// 存在新線程嘗試獲取共享鎖,則cachedHoldCounter必定爲null
HoldCounter rh = cachedHoldCounter;
if (rh == null || rh.tid != getThreadId(current))
// readHolds集成自ThreadLocal,爲每一個新線程分配一個HoldCounter
cachedHoldCounter = rh = readHolds.get();
// 可能存在其他途徑創建線程的HoldCounter,這樣在readHolds中可能不存在(個人理解)
else if (rh.count == 0)
readHolds.set(rh);
// 初始化完成後,對count遞增,表示線程加鎖及重入
rh.count++;
}
return true;
}
}
}
* readHolds.get():此處涉及 ThreadLocal 的初始化部分
// ThreadLocalHoldCounter繼承自ThreadLocal
// 泛型表示ThreadLocal爲每一個線程分配一個 HoldCounter 對象
// 當前類重寫了父類的initialValue()方法,並初始化了一個HoldCounter對象
// 表示 ThreadLocalHoldCounter 默認爲每一個線程分配一個已經初始化好的對象
// 每一次get時,如果獲取不到線程已經存儲的信息,則直接返回一個新對象
static final class ThreadLocalHoldCounter extends ThreadLocal<HoldCounter> {
public HoldCounter initialValue() {
return new HoldCounter();
}
}
2,加鎖
* lock()
public void lock() {
sync.acquireShared(1);
}
public final void acquireShared(int arg) {
if (tryAcquireShared(arg) < 0)
doAcquireShared(arg);
}
* tryAcquireShared(int unused)
protected final int tryAcquireShared(int unused) {
Thread current = Thread.currentThread();
int c = getState();
// 存在寫鎖,並且不是當前線程獲取,則獲取鎖失敗
// 此處不排除存在寫鎖,如果存在寫鎖則寫鎖爲當前線程持有
if (exclusiveCount(c) != 0 &&
getExclusiveOwnerThread() != current)
return -1;
// readerShouldBlock():判斷是否存在下一個節點,且下一個節點不是共享節點
int r = sharedCount(c);
if (!readerShouldBlock() &&
r < MAX_COUNT &&
compareAndSetState(c, c + SHARED_UNIT)) {
// 下面爲加鎖處理
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;
}
// 繼續嘗試獲取共享鎖
return fullTryAcquireShared(current);
}
* fullTryAcquireShared(Thread current)
final int fullTryAcquireShared(Thread current) {
HoldCounter rh = null;
for (;;) {
int c = getState();
// 判斷是否獨佔鎖,且不是當前線程
// 當前線程獲取獨佔鎖後,可繼續獲取共享鎖
if (exclusiveCount(c) != 0) {
if (getExclusiveOwnerThread() != current)
return -1;
// 存在下一個節點,並且下一個不是共享鎖節點,則需要阻塞
} else if (readerShouldBlock()) {
// firstReader表示當前線程,說明正在執行中,默認不阻塞
if (firstReader == current) {
} else {
// 此處判斷當前線程是否在執行中,rh.count表示重入
// 如果在執行中,則繼續可以獲取執行,如果不在執行中,則獲取失敗
if (rh == null) {
rh = cachedHoldCounter;
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)) {
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;
}
}
}
* readerShouldBlock():存在下一個節點,並且下一個節點不是共享節點,返回true,否則返回false
final boolean readerShouldBlock() {
return apparentlyFirstQueuedIsExclusive();
}
// 此處判斷是否存在下一個節點,以及下一個節點是否爲共享節點(取反)
final boolean apparentlyFirstQueuedIsExclusive() {
Node h, s;
return (h = head) != null &&
(s = h.next) != null &&
!s.isShared() &&
s.thread != null;
}
3,釋放鎖
* unlock()
public void unlock() {
sync.releaseShared(1);
}
public final boolean releaseShared(int arg) {
if (tryReleaseShared(arg)) {
doReleaseShared();
return true;
}
return false;
}
* tryReleaseShared(int unused)
protected final boolean tryReleaseShared(int unused) {
Thread current = Thread.currentThread();
// 判斷當前線程是否firstReader
// 此處條件判斷,主要爲了遞減線程重入次數,如果可以釋放,則對關鍵標識對象置空,幫助GC,並方便後續操作判斷
if (firstReader == current) {
// 繼續判斷firstReaderHoldCount重入次數,如果爲1,說明可以徹底釋放,重置firstReader
// 不爲1,遞減即可
if (firstReaderHoldCount == 1)
firstReader = null;
else
firstReaderHoldCount--;
} else {
// 當前線程不是firstReader,則從readHolds中獲取,並判斷count重入次數
HoldCounter rh = cachedHoldCounter;
if (rh == null || rh.tid != getThreadId(current))
rh = readHolds.get();
int count = rh.count;
// count <=1,同樣表示徹底釋放,從readHolds中移除
if (count <= 1) {
readHolds.remove();
if (count <= 0)
throw unmatchedUnlockException();
}
--rh.count;
}
for (;;) {
// 獲取共享鎖的重入次數,並遞減,是否爲0標識
int c = getState();
int nextc = c - SHARED_UNIT;
if (compareAndSetState(c, nextc))
return nextc == 0;
}
}
五,ReentrantReadWriteLock.WriteLock源碼分析
1,嘗試加鎖
* tryLock( )
public boolean tryLock( ) {
return sync.tryWriteLock();
}
* tryWriteLock()
final boolean tryWriteLock() {
Thread current = Thread.currentThread();
int c = getState();
// c不爲0,表示存在獨佔鎖或者共享鎖
if (c != 0) {
// 獲取獨佔鎖重入次數
int w = exclusiveCount(c);
// w爲0,說明獨佔鎖爲空,則表示存在共享鎖,獲取鎖失敗
// w不爲0,並且鎖線程不是當前線程,說明存在其他線程持有獨佔鎖,獲取鎖失敗
// 此處證明在同一線程下,獲取共享鎖後,不可獲取獨佔鎖
if (w == 0 || current != getExclusiveOwnerThread())
return false;
if (w == MAX_COUNT)
throw new Error("Maximum lock count exceeded");
}
// CAS設置重入狀態,設置成功後,添加鎖線程爲當前線程
if (!compareAndSetState(c, c + 1))
return false;
setExclusiveOwnerThread(current);
return true;
}
2,加鎖
* lock()
public void lock() {
sync.acquire(1);
}
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
* tryAcquire(int acquires):此處嘗試加鎖與上一步嘗試加鎖基本一致,只是加了一步判斷
protected final boolean tryAcquire(int acquires) {
Thread current = Thread.currentThread();
int c = getState();
int w = exclusiveCount(c);
if (c != 0) {
if (w == 0 || current != getExclusiveOwnerThread())
return false;
if (w + exclusiveCount(acquires) > MAX_COUNT)
throw new Error("Maximum lock count exceeded");
setState(c + acquires);
return true;
}
// writerShouldBlock:判斷寫鎖是否需要阻塞
if (writerShouldBlock() ||
!compareAndSetState(c, c + acquires))
return false;
setExclusiveOwnerThread(current);
return true;
}
* writerShouldBlock():該方法在非公平鎖中默認返回false,再公平鎖實現中,進行了AQS隊列存在性校驗,保證順序執行
// 非公平鎖
final boolean writerShouldBlock() {
return false;
}
// 公平鎖
final boolean writerShouldBlock() {
return hasQueuedPredecessors();
}
public final boolean hasQueuedPredecessors() {
Node t = tail;
Node h = head;
Node s;
return h != t &&
((s = h.next) == null || s.thread != Thread.currentThread());
}
3,釋放鎖
* unlock()
public void unlock() {
sync.release(1);
}
public final boolean release(int arg) {
if (tryRelease(arg)) {
Node h = head;
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);
return true;
}
return false;
}
* tryRelease(int releases)
protected final boolean tryRelease(int releases) {
// 判斷當前線程是否鎖線程
if (!isHeldExclusively())
throw new IllegalMonitorStateException();
// 獲取釋放後的獨佔鎖重入次數
int nextc = getState() - releases;
boolean free = exclusiveCount(nextc) == 0;
// 重入次數爲0,則釋放成功,置空鎖線程,
if (free)
setExclusiveOwnerThread(null);
setState(nextc);
return free;
}