一、前言
在Java
併發包中常用的鎖(如:ReentrantLock
),基本上都是排他鎖,這些鎖在同一時刻只允許一個線程進行訪問,而讀寫鎖在同一時刻可以允許多個讀線程訪問,但是在寫線程訪問時,所有的讀線程和其他寫線程均被阻塞。讀寫鎖維護了一對鎖,一個讀鎖和一個寫鎖,通過分離讀鎖和寫鎖,使得併發性相比一般的排他鎖有了很大提升。
相對於排他鎖,提高了併發性。在實際應用中,大部分情況下對共享數據(如緩存)的訪問都是讀操作遠多於寫操作,這時ReentrantReadWriteLock
能夠提供比排他鎖更好的併發性和吞吐量。
二、ReentrantReadWriteLock
1、ReentrantReadWriteLock類包含三個核心變量:
- readerLock:讀鎖。
- writerLock:寫鎖。
- sync:可以爲公平鎖FairSync 或 非公平鎖NonfairSync。
2、ReentrantReadWriteLock類的構造函數執行以下幾件事情:
- 新建鎖對象賦予ReentrantReadWriteLock的sync對象。
- 將ReentrantReadWriteLock對象作爲this參數創建讀鎖ReadLock。
- 將ReentrantReadWriteLock對象作爲this參數創建寫鎖WriteLock。
- 在writerLock和ReadLock構造函數中將ReentrantReadWriteLock的sync對象賦值爲自身的sync對象,讀寫鎖的操作其實用的就是ReentrantReadWriteLock的sync對象。
// ReentrantReadWriteLock本身不提供加鎖服務,只負責提供讀鎖和寫鎖
public class ReentrantReadWriteLock implements ReadWriteLock, java.io.Serializable {
private final ReentrantReadWriteLock.ReadLock readerLock;
private final ReentrantReadWriteLock.WriteLock writerLock;
final Sync sync;
public ReentrantReadWriteLock() {
this(false);
}
public ReentrantReadWriteLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
readerLock = new ReadLock(this);
writerLock = new WriteLock(this);
}
public ReentrantReadWriteLock.WriteLock writeLock() { return writerLock; } // 寫鎖
public ReentrantReadWriteLock.ReadLock readLock() { return readerLock; } // 讀鎖
// 總的讀鎖計數
public int getReadLockCount() {
return sync.getReadLockCount();
}
// 當前線程的寫鎖計數(當前線程必須佔有鎖)
public int getWriteHoldCount() {
return sync.getWriteHoldCount();
}
// 當前線程的讀鎖計數
public int getReadHoldCount() {
return sync.getReadHoldCount();
}
// 獲取所有在SyncQueue中排隊的寫線程
protected Collection<Thread> getQueuedWriterThreads() {
return sync.getExclusiveQueuedThreads();
}
// 獲取所有在SyncQueue中排隊的讀線程
protected Collection<Thread> getQueuedReaderThreads() {
return sync.getSharedQueuedThreads();
}
... ...
static final long getThreadId(Thread thread) {
return UNSAFE.getLongVolatile(thread, TID_OFFSET); // tid在Thread類中非volatile
}
private static final sun.misc.Unsafe UNSAFE;
private static final long TID_OFFSET;
static {
try {
UNSAFE = sun.misc.Unsafe.getUnsafe();
Class<?> tk = Thread.class;
TID_OFFSET = UNSAFE.objectFieldOffset
(tk.getDeclaredField("tid"));
} catch (Exception e) {
throw new Error(e);
}
}
}
3、ReadLock
- ReadLock的構造函數入參爲ReentrantReadWriteLock對象,通過將ReentrantReadWriteLock對象的sync對象賦值給ReadLock的Sync。
- ReadLock的lock和unlock操作都通過sync對象來實現加鎖和解鎖。
public static class ReadLock implements Lock, java.io.Serializable {
private static final long serialVersionUID = -5992448646407690164L;
private final Sync sync;
protected ReadLock(ReentrantReadWriteLock lock) {
sync = lock.sync;
}
public void lock() { // 加讀鎖
sync.acquireShared(1);
}
public void lockInterruptibly() throws InterruptedException { // 加讀鎖(可被中斷)
sync.acquireSharedInterruptibly(1);
}
public boolean tryLock() { // 嘗試加讀鎖
return sync.tryReadLock();
}
public boolean tryLock(long timeout, TimeUnit unit)
throws InterruptedException { // 加讀鎖(在timeOut內等鎖)
return sync.tryAcquireSharedNanos(1, unit.toNanos(timeout));
}
public void unlock() { // 釋放讀鎖
sync.releaseShared(1);
}
public Condition newCondition() { // 拋出不支持的條件異常
throw new UnsupportedOperationException();
}
public String toString() { // 返回標識此鎖的字符串及其鎖定狀態
int r = sync.getReadLockCount();
return super.toString() +
"[Read locks = " + r + "]";
}
}
4、WriteLock
- WriteLock的構造函數入參爲ReentrantReadWriteLock對象,通過將ReentrantReadWriteLock對象的sync對象賦值給ReadLock的Sync。
- WriteLock的lock和unlock操作都通過sync對象來實現加鎖和解鎖。
public static class WriteLock implements Lock, java.io.Serializable {
private final Sync sync;
protected WriteLock(ReentrantReadWriteLock lock) {
sync = lock.sync;
}
public void lock() { // 加寫鎖
sync.acquire(1);
}
public void lockInterruptibly() throws InterruptedException { // 加寫鎖(可被中斷)
sync.acquireInterruptibly(1);
}
public boolean tryLock( ) { // 嘗試加寫鎖
return sync.tryWriteLock();
}
public boolean tryLock(long timeout, TimeUnit unit) throws InterruptedException { // 加寫鎖(在timeOut內等鎖)
return sync.tryAcquireNanos(1, unit.toNanos(timeout));
}
public void unlock() { // 釋放寫鎖
sync.release(1);
}
public Condition newCondition() {
return sync.newCondition();
}
public String toString() { // 返回標識此鎖的字符串及其lock狀態
Thread o = sync.getOwner();
return super.toString() + ((o == null) ?
"[Unlocked]" :
"[Locked by thread " + o.getName() + "]");
}
5、Sync
- 公平鎖FairSync和非公平鎖NonfairSync屬於ReentrantReadWriteLock內部定義類,繼承自Sync類。
- Sync類繼承自AbstractQueuedSynchronizer類,Sync類有一些核心的變量已經加註釋。
- AQS的state變量32位分開高16位爲讀狀態,低16位爲寫狀態。
static final class NonfairSync extends Sync {
private static final long serialVersionUID = -8159625535654395037L;
final boolean writerShouldBlock() { // 寫線程永遠可以搶鎖
return false;
}
final boolean readerShouldBlock() { // SyncQueue排隊的第一個節點(head.next)爲寫節點佔鎖時必須排隊
return apparentlyFirstQueuedIsExclusive();
}
}
final boolean apparentlyFirstQueuedIsExclusive() {
Node h, s;
return (h = head) != null &&
(s = h.next) != null &&
!s.isShared() &&
s.thread != null;
}
static final class FairSync extends Sync {
private static final long serialVersionUID = -2274990926593161451L;
final boolean writerShouldBlock() { // SyncQueue中下個待喚醒節點不是當前線程時必須排隊
return hasQueuedPredecessors();
}
final boolean readerShouldBlock() { // SyncQueue中下個待喚醒節點不是當前線程時必須排隊
return hasQueuedPredecessors();
}
}
public final boolean hasQueuedPredecessors() {
Node t = tail; // Read fields in reverse initialization order
Node h = head;
Node s;
return h != t &&
((s = h.next) == null || s.thread != Thread.currentThread());
}
static final class Node {
// 正在等待的共享鎖
static final Node SHARED = new Node();
// 正在等待的獨佔鎖
static final Node EXCLUSIVE = null;
// 等待狀態取消
static final int CANCELLED = 1;
// 等待狀態喚醒
static final int SIGNAL = -1;
// 等待狀態條件
static final int CONDITION = -2;
// 無條件傳播
static final int PROPAGATE = -3;
// 等待狀態
volatile int waitStatus;
// 前驅節點
volatile Node prev;
// 後驅節點
volatile Node next;
volatile Thread thread;
// 鏈接到等待條件的下一個節點
Node nextWaiter;
// 如果節點在共享模式下等待,則返回true
final boolean isShared() {
return nextWaiter == SHARED;
}
// 返回上一個節點,如果爲null則拋出NullPointerException。
final Node predecessor() throws NullPointerException {
Node p = prev;
if (p == null)
throw new NullPointerException();
else
return p;
}
Node() { // 用於初始頭部和共享標記
}
Node(Thread thread, Node mode) { // 由addWaiter模式使用
this.nextWaiter = mode;
this.thread = thread;
}
Node(Thread thread, int waitStatus) { // 由條件使用
this.waitStatus = waitStatus;
this.thread = thread;
}
}
核心類Sync,,這個類小夥伴們可以多花點時間研究下。
讀寫狀態的設計
讀寫鎖同樣依賴自定義同步器來實現同步功能,而讀寫狀態就是其同步器的同步狀態。回想ReentrantLock中自定義同步器的實現,同步狀態表示鎖被一個線程重複獲取的次數,而讀寫鎖的自定義同步器需要在同步狀態(一個整型變量)上維護多個讀線程和一個寫線程的狀態,使得該狀態的設計成爲讀寫鎖實現的關鍵。
如果在一個整型變量上維護多種狀態,就一定需要“按位切割使用”這個變量,讀寫鎖是將變量切分成了兩個部分,高16位表示讀,低16位表示寫,劃分方式如圖所示。
如圖所示,當前同步狀態表示一個線程已經獲取了寫鎖,且重進入了兩次,同時也連續獲取了兩次讀鎖。讀寫鎖是如何迅速的確定讀和寫各自的狀態呢?答案是通過位運算。假設當前同步狀態值爲S,寫狀態等於 S & 0x0000FFFF(將高16位全部抹去),讀狀態等於 S >>> 16(無符號補0右移16位)。當寫狀態增加1時,等於S + 1,當讀狀態增加1時,等於S + (1 << 16),也就是S + 0x00010000。
根據狀態的劃分能得出一個推論:S不等於0時,當寫狀態(S & 0x0000FFFF)等於0時,則讀狀態(S >>> 16)大於0,即讀鎖已被獲取。
abstract static class Sync extends AbstractQueuedSynchronizer {
static final int SHARED_SHIFT = 16; // state高16位爲讀鎖計數,state低16位爲寫鎖計數
static final int SHARED_UNIT = (1 << SHARED_SHIFT); // 讀鎖計數 + 1
static final int MAX_COUNT = (1 << SHARED_SHIFT) - 1; // 讀(寫)鎖計數 <= 2^16 - 1
static final int EXCLUSIVE_MASK = (1 << SHARED_SHIFT) - 1; // 寫鎖計數 = state & (2^16 - 1)
Sync() {
readHolds = new ThreadLocalHoldCounter();
setState(getState()); // 確保readHolds的可見性
}
// return 鎖狀態(鎖計數)
final int getCount() { return getState(); }
// 總的讀鎖計數
final int getReadLockCount() {
return sharedCount(getState());
}
static int sharedCount(int c) { return c >>> SHARED_SHIFT; }
// 當前線程的寫鎖計數(當前線程必須佔有鎖)
final int getWriteHoldCount() {
return isHeldExclusively() ? exclusiveCount(getState()) : 0;
}
static int exclusiveCount(int c) { return c & EXCLUSIVE_MASK; }
// 當前線程的讀鎖計數
final int getReadHoldCount() {
if (getReadLockCount() == 0)
return 0;
Thread current = Thread.currentThread();
if (firstReader == current)
return firstReaderHoldCount;
HoldCounter rh = cachedHoldCounter;
if (rh != null && rh.tid == getThreadId(current))
return rh.count;
int count = readHolds.get().count;
if (count == 0) readHolds.remove();
return count;
}
private transient ThreadLocalHoldCounter readHolds; // 當前線程的讀鎖計數
private transient HoldCounter cachedHoldCounter; // 緩存的線程讀鎖計數
private transient Thread firstReader = null; // 首個讀線程
private transient int firstReaderHoldCount; // 首個讀線程的讀鎖計數
static final class HoldCounter {
int count = 0;
// tid在Thread類中非volatile
// Thread.getId -> UNSAFE.getLongVolatile(Thread.currentThread(), Thread.class.getDeclaredField("tid"))
final long tid = getThreadId(Thread.currentThread());
}
static final class ThreadLocalHoldCounter extends ThreadLocal<HoldCounter> {
public HoldCounter initialValue() {
return new HoldCounter(); // {0, currentThreadId}
}
}
// 嘗試取寫鎖
protected final boolean tryAcquire(int acquires) {
Thread current = Thread.currentThread();
int c = getState(); /*記錄state*/
int w = exclusiveCount(c); // 寫鎖計數w
if (c != 0) {// 讀線程未釋放鎖 || 當前線程不是鎖的獨佔者
if (w == 0 || current != getExclusiveOwnerThread())
return false; // 取鎖失敗
// 當前線程已佔有鎖(Reentrant)
if (w + exclusiveCount(acquires) > MAX_COUNT)
throw new Error("Maximum lock count exceeded");
setState(c + acquires); // 更新鎖狀態,寫鎖計數++
return true; // 成功取鎖(已佔有鎖)
}
// state == 0
if (writerShouldBlock() || // false(NonfairSync)|| hasQueuedPredecessors(FairSync)
!compareAndSetState(c, c + acquires)) /*CAS設置state += acquires*/
// CAS(state)失敗
return false; // 取鎖失敗
// CAS(state)成功
setExclusiveOwnerThread(current); // 設置當前線程爲鎖的獨佔者
return true; // 成功取鎖
}
// 嘗試搶寫鎖,不進行writerShouldBlock判斷
final boolean tryWriteLock() {
Thread current = Thread.currentThread();
int c = getState();
if (c != 0) {
int w = exclusiveCount(c); // 寫鎖計數w
// 讀線程未釋放鎖 || 當前線程不是鎖的獨佔者
if (w == 0 || current != getExclusiveOwnerThread())
return false;
// 當前線程已佔有鎖(Reentrant)
if (w == MAX_COUNT)
throw new Error("Maximum lock count exceeded");
}
if (!compareAndSetState(c, c + 1)) // CAS設置state += 1
// CAS(state)失敗
return false; // 搶鎖失敗
// CAS(state)成功
setExclusiveOwnerThread(current);
return true; // 搶鎖成功
}
// 嘗試取讀鎖
protected final int tryAcquireShared(int unused) {
Thread current = Thread.currentThread();
int c = getState(); /*記錄state*/
// 寫線程未釋放鎖 && 未釋放鎖的寫線程非當前線程(讀寫線程可爲同一線程)
if (exclusiveCount(c) != 0 && getExclusiveOwnerThread() != current)
return -1; // 取鎖失敗
int r = sharedCount(c); // 讀鎖計數r
if (!readerShouldBlock() && // apparentlyFirstQueuedIsExclusive(NonfairSync)|| hasQueuedPredecessors(FairSync)
r < MAX_COUNT && // r未超過閾值
compareAndSetState(c, c + SHARED_UNIT)) { /*CAS設置state += SHARED_UNIT*/ // 讀鎖計數++
// CAS(state)成功
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) // 緩存的是當前線程的讀鎖計數,而當前線程的讀鎖計數在上次release時被刪除
readHolds.set(rh);
rh.count++;
}
return 1; // 成功取鎖
}
// CAS(state)失敗
return fullTryAcquireShared(current);
}
// 不斷嘗試取讀鎖,直到取鎖失敗(寫線程佔有鎖 || 當前線程的讀鎖計數爲0) || 成功取鎖
final int fullTryAcquireShared(Thread current) {
HoldCounter rh = null;
for (;;) {
// 當前線程的讀鎖計數不爲0 && CAS(state)失敗將回到此處
int c = getState(); /*記錄state*/
if (exclusiveCount(c) != 0) { // 寫線程未釋放鎖
if (getExclusiveOwnerThread() != current) // 當前線程不是鎖的獨佔者
return -1;// 取鎖失敗
} else if (readerShouldBlock()) {
if (firstReader == current) {
} else {
if (rh == null) {
rh = cachedHoldCounter;
if (rh == null || rh.tid != getThreadId(current)) {
rh = readHolds.get();
if (rh.count == 0) // 當前線程的讀鎖計數爲0
readHolds.remove(); // 在線程局部變量中刪除當前線程的讀鎖計數
}
}
if (rh.count == 0) // 當前線程的讀鎖計數爲0
return -1; // 應排隊取鎖
// 繼續嘗試取鎖
}
}
if (sharedCount(c) == MAX_COUNT)
throw new Error("Maximum lock count exceeded");
if (compareAndSetState(c, c + SHARED_UNIT)) { /*CAS設置state += SHARED_UNIT*/
// CAS(state)成功
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; // 成功取鎖
}
// CAS(state)失敗
}
}
// 嘗試搶讀鎖,不進行readerShouldBlock判斷
final boolean tryReadLock() {
Thread current = Thread.currentThread();
for (;;) {
// CAS(state)失敗將回到此處
int c = getState(); /*記錄state*/
// 寫線程未釋放鎖 && 未釋放鎖的寫線程非當前線程(讀寫線程可爲同一線程)
if (exclusiveCount(c) != 0 && getExclusiveOwnerThread() != current)
return false; // 搶鎖失敗
int r = sharedCount(c);
if (r == MAX_COUNT)
throw new Error("Maximum lock count exceeded");
if (compareAndSetState(c, c + SHARED_UNIT)) { /*CAS設置state += SHARED_UNIT*/
// CAS(state)成功
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 true; // 成功搶鎖
}
// CAS(state)失敗
}
}
// 嘗試釋放寫鎖
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;
}
// 嘗試釋放讀鎖
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 (;;) {
// CAS(state)失敗將回到此處
int c = getState(); /*記錄state*/
int nextc = c - SHARED_UNIT; // 讀鎖計數--
if (compareAndSetState(c, nextc)) /*CAS設置state = nextc*/
// CAS(state)成功
return nextc == 0;
// CAS(state)失敗
}
}
... ...
}
下一篇 中篇主要來講解講解ReentrantReadWriteLock
的ReadLock
的加鎖和減鎖過程:
源碼解讀之(五)ReentrantReadWriteLock(中)