JDK8引入
主要是有樂觀讀, 就是完全無鎖, 讀到以后再去檢查下版本, 不行再說
好處
比ReentrantReadWriteLock
的改進(jìn)就是加了樂觀讀, 這樣了避免了 ReentrantReadWriteLock
非公平鎖時(shí) 讀多寫少, 寫入隊(duì)以后一直輪不到,餓死的情況, 還提高了吞吐量
另外, 可以升級(jí) 也可以降級(jí)
壞處
和ReentrantReadWriteLock
比, 功能少了, 只是一個(gè)子集
- 不支持條件變量
- 不能中斷線程
- 不能重入
而且 ,本來是為內(nèi)部使用的, 用起來一定要按模板,比較復(fù)雜
樂觀讀
不能保證讀到是最新的, 可以拿到以后再驗(yàn)證, 完全無鎖, 性能好
使用 都是這個(gè)模板
public String get(Integer key) {
// 1. 嘗試通過樂觀讀模式讀取數(shù)據(jù),非阻塞
long stamp = lock.tryOptimisticRead();
// 2. 讀取數(shù)據(jù)到當(dāng)前線程棧
String currentValue = idMap.get(key);
// 3. 校驗(yàn)是否被其他線程修改過,true 表示未修改,否則需要加悲觀讀鎖
if (!lock.validate(stamp)) {
// 4. 上悲觀讀鎖,并重新讀取數(shù)據(jù)到當(dāng)前線程局部變量
stamp = lock.readLock();
try {
currentValue = idMap.get(key);
} finally {
lock.unlockRead(stamp);
}
}
// 5. 若校驗(yàn)通過,則直接返回?cái)?shù)據(jù)
return currentValue;
}
原理
比ReentrantReadWriteLock
性能要高,內(nèi)部沒用AQS 但是 和AQS原理是一樣的 都是用的 CLH隊(duì)列
一樣是雙向鏈表, 有存了 頭尾 字段, 有個(gè)狀態(tài)
state
也是狀態(tài)2 用, 又表示寫鎖又記讀鎖
注意有個(gè)NCPU
字段, 記錄的是核數(shù), 如果核數(shù)>1 , 獲取鎖和入隊(duì) 都有自旋
CLH隊(duì)列的節(jié)點(diǎn)比AQS更加簡(jiǎn)單, 只有3種狀態(tài)
另外多了一個(gè) cowait
字段, 記錄讀線程, 頭插法
這樣 如果前一個(gè)節(jié)點(diǎn)就是讀, 下一個(gè)讀就連入
cowait
了, 不像AQS往后連,而且因?yàn)槭穷^插法, 晚來的讀 反而先叫起來, 不過反正不讀之間不阻塞, 差不多
得鎖 解鎖
獲取寫鎖:
cas試試, 失敗就入隊(duì),
入隊(duì)有大量自旋操作, 就是入前文所述的 CLH
讀鎖也差不多
釋放鎖:
和ReentrantReadWriteLock
差不多 都改state, 叫醒隊(duì)列下一個(gè), 就是前面多了一步, 核對(duì)傳入的stamp
, 如果對(duì)不上會(huì)拋出異常