讀寫鎖ReentrantReadWriteLock
讀鎖為共享鎖,而寫鎖則為獨佔鎖,同時他們用的是一個同步器,同步器中不同的內部類實現了相應的功能
AQS中有一個state欄位(int型別,32位)用來描述有多少執行緒獲持有鎖。在獨佔鎖的時代這個值通常是0或者1(如果是重入的就是重入的次數),在共享鎖的時代就是持有鎖的數量。ReadWriteLock的讀、寫鎖是相關但是又不一致的,所以需要兩個數來描述讀鎖(共享鎖)和寫鎖(獨佔鎖)的數量。顯然現在一個state就不夠用了。於是在ReentrantReadWrilteLock裡面將這個欄位一分為二,高位16位表示共享鎖的數量,低位16位表示獨佔鎖的數量(或者重入數量)。2^16-1=65536,這就是上節中提到的為什麼共享鎖和獨佔鎖的數量最大隻能是65535的原因了。
1.在獨佔數量上則採用了return c & EXCLUSIVE_MASK
EXCLUSIVE_MASK = (1 << SHARED_SHIFT) - 1; 表示向左移動16位 然後減掉1,即低16位全為1,高16位全為0
即通過 & 00000000000000001111111111111111的形式獲取低16位
通過與操作就能知道states變數的低16的值,也即獨佔鎖數量
2.共享鎖數量上則採用了往右移16位的形式,最高位都補0,
即11111111111111111111111111111111變成了類似00000000000000001111111111111111的形式獲取高16位
寫鎖的獲取
一、
c!=0說明有鎖了,
1.w==0成立說明有多個讀鎖在用了,這時不能獲取寫鎖,因為寫鎖將導致資料亂掉
2.w!=0 成立 說明當前有寫鎖,如果該執行緒進來競爭寫鎖又不是當前執行緒的話則不行,他們是獨佔的,同時如果寫鎖進來後加起來超過了65535(即16位都為1的情況下再進來一個寫鎖的話)則報錯,超出了能記錄的鎖數量限制
二、
c==0說明沒有任何的鎖,
前面判斷寫是不是需要阻塞(這個是公平鎖和非公平鎖的阻塞情況),
下面列的是公平鎖的,非公平鎖就直接返回了false
如果AQS佇列不為空或者當前執行緒不在佇列頭部那麼就阻塞佇列(即加入到同步佇列中自身不斷自旋)
後面判斷獲取寫鎖,失敗直接false,其他則true
寫鎖的釋放
判斷是否存在重入的情況,如果是重入的話不能將當前的執行緒資訊清空
讀鎖的獲取
1.首先判斷是否存在寫鎖 同時當前執行緒是否為那個執行緒,如果不是拜拜,如果是的話則到2(允許寫鎖的執行緒同時獲取讀鎖)
2.讀是否阻塞,讀鎖數量有沒有超過,那麼讀鎖就CAS +1,如果判斷沒通過自旋去吧
讀鎖釋放
不斷的自旋CAS
讀鎖裡面存在一個特別的HoldCounter,因為讀鎖是共享的,存在多個讀鎖的情況,同時釋放的時候其實是類似計數器的形式,為了避免一個執行緒操作了一個不屬於自己監聽器物件的異常,也就是一個執行緒釋放了一個不屬於自己的或不存在的共享鎖就引入了HoldCounter
HoldCounter的作用後我們就可以猜到它的作用其實就是當前執行緒持有共享鎖(讀取鎖)的數量,包括重入的數量。那麼這個數量就必須和執行緒繫結在一起,表示執行緒持有讀取鎖數量的計數器。可以看到這裡使用ThreadLocal將HoldCounter繫結到當前執行緒上,同時HoldCounter也持有執行緒Id,這樣在釋放鎖的時候才能知道ReadWriteLock裡面快取的上一個讀取執行緒(cachedHoldCounter)是否是當前執行緒。這樣做的好處是可以減少ThreadLocal.get()的次數,因為這也是一個耗時操作。需要說明的是這樣HoldCounter繫結執行緒id而不繫結執行緒物件的原因是避免HoldCounter和ThreadLocal互相繫結而GC難以釋放它們(儘管GC能夠智慧的發現這種引用而回收它們,但是這需要一定的代價),所以其實這樣做只是為了幫助GC快速回收物件而已。
相關文章
- 讀寫鎖 ReentrantReadWriteLock
- 讀寫鎖 ReentrantReadWriteLock 與 互斥鎖 的效率
- ReentrantReadWriteLock讀寫鎖及其在 RxCach
- Java併發——讀寫鎖ReentrantReadWriteLockJava
- 深入理解讀寫鎖ReentrantReadWriteLock
- Lock介面、重入鎖ReentrantLock、讀寫鎖ReentrantReadWriteLockReentrantLock
- Java 讀寫鎖 ReentrantReadWriteLock 原始碼分析Java原始碼
- 原始碼分析:ReentrantReadWriteLock之讀寫鎖原始碼
- ReentrantReadWriteLock讀寫鎖及其在 RxCache 中的使用
- Java併發程式設計-讀寫鎖(ReentrantReadWriteLock)Java程式設計
- AQS之ReentrantReadWriteLock寫鎖AQS
- Java併發程式設計之鎖機制之ReentrantReadWriteLock(讀寫鎖)Java程式設計
- 輕鬆掌握java讀寫鎖(ReentrantReadWriteLock)的實現原理Java
- Java併發指南10:Java 讀寫鎖 ReentrantReadWriteLock 原始碼分析Java原始碼
- Java併發包原始碼學習系列:ReentrantReadWriteLock讀寫鎖解析Java原始碼
- Java中的讀寫鎖ReentrantReadWriteLock詳解,存在一個小缺陷Java
- java原始碼-ReentrantReadWriteLock寫鎖介紹Java原始碼
- 深刨顯式鎖ReentrantLock原理及其與內建鎖的區別,以及讀寫鎖ReentrantReadWriteLock使用場景ReentrantLock
- Java鎖之ReentrantReadWriteLockJava
- Java使用讀寫鎖替代同步鎖Java
- MySQL MyISAM引擎的讀鎖與寫鎖MySql
- Java讀寫鎖ReadWriteLockJava
- Go語言之讀寫鎖Go
- Java中的讀/寫鎖Java
- 淺談Java中的鎖:Synchronized、重入鎖、讀寫鎖Javasynchronized
- 簡單分析執行緒獲取ReentrantReadWriteLock 讀鎖的規則執行緒
- c++中的讀寫鎖C++
- Java併發-顯式鎖篇【可重入鎖+讀寫鎖】Java
- 併發技術4:讀寫鎖
- Golang 讀寫鎖RWMutex 互斥鎖Mutex 原始碼詳解GolangMutex原始碼
- Go語言中的互斥鎖和讀寫鎖(Mutex和RWMutex)GoMutex
- Concurrency(十五: Java中的讀寫鎖)Java
- golang RWMutex讀寫互斥鎖原始碼分析GolangMutex原始碼
- 從自旋鎖、睡眠鎖、讀寫鎖到 Linux RCU 機制講解Linux
- 【JavaSE】Lock鎖和synchronized鎖的比較,lock鎖的特性,讀寫鎖的實現。Javasynchronized
- 多執行緒與併發----讀寫鎖執行緒
- MySQL內部實現讀鎖和寫鎖的具體鎖定型別介紹MySql型別
- Golang 基礎值速學之二十一(讀寫鎖互斥鎖)Golang