本文從源碼角度解析讀寫鎖,其實讀寫鎖實現(xiàn)并不復雜,真正的難點還是AQS缓苛。如果對這方面不熟悉可以參考這篇文章AQS
ReentrantLock實現(xiàn)的是獨占鎖校摩,是一種互斥操作看峻,也就是說一次只能允許一個線程獲取鎖。而這種鎖在某些程度上會降低線程的吞吐量衙吩,比如當我只讀取數(shù)據(jù)時不會更改內(nèi)存數(shù)據(jù)互妓,所以加鎖就是一種負擔。所以獨占鎖也是一種保守的線程安全策略坤塞。而J.U.C中的讀寫鎖實現(xiàn)了讀鎖/寫鎖的分離冯勉。(注意:鎖分離并不是說用了兩個鎖。仍然是一個鎖摹芙,不過這個鎖允許多線程同時讀灼狰,但是不允許“讀寫”或者“寫寫”
ReadWriteLock定義了獲取讀鎖和寫鎖的方法,具體還是在ReentrantReadWriteLock中實現(xiàn)
public interface ReadWriteLock {
Lock readLock();
Lock writeLock();
}
讀寫鎖實現(xiàn)分析
- 讀寫狀態(tài)的分析
閱讀過AQS我們都知道瘫辩,AQS用state(整形變量)屬性表示線程的同步狀態(tài)伏嗜。而讀寫鎖上維護多個讀線程或者一個寫線程的狀態(tài)。一個整形要怎么實現(xiàn)呢伐厌?
如果要用一個整形的state屬性就要維護多種狀態(tài)承绸,就要”按位使用“,讀寫鎖中將各變量分成了兩部分挣轨。高16位表示讀狀態(tài)军熏,低16位表示寫狀態(tài)。 - 讀鎖的獲取與釋放
我們首先來看ReentrantReadWriteLock中的tryAcquireShared方法
protected final int tryAcquireShared(int unused) {
Thread current = Thread.currentThread();
int c = getState();
if (exclusiveCount(c) != 0 && getExclusiveOwnerThread() != current)
//判斷是否為獨占鎖(寫鎖)卷扮,如果是荡澎,判斷是否為當前線程獲取鎖
return -1; //獲取鎖失敗
if (sharedCount(c) == MAX_COUNT)
throw new Error("Maximum lock count exceeded");
if (!readerShouldBlock(current) && compareAndSetState(c, c + SHARED_UNIT)) {
//如果讀線程不用等待(和是否公平有關系)并且設置狀態(tài)成功
HoldCounter rh = cachedHoldCounter;
if (rh == null || rh.tid != current.getId())
cachedHoldCounter = rh = readHolds.get();
rh.count++;
return 1;//獲取鎖成功
}
return fullTryAcquireShared(current);//到這里說明讀鎖要等待或者設置狀態(tài)未成功
}
final int fullTryAcquireShared(Thread current) {//不斷嘗試獲取鎖
HoldCounter rh = cachedHoldCounter;
if (rh == null || rh.tid != current.getId())
rh = readHolds.get();
for (;;) {
int c = getState();
int w = exclusiveCount(c);
if ((w != 0 && getExclusiveOwnerThread() != current) ||
((rh.count | w) == 0 && readerShouldBlock(current)))
return -1;
if (sharedCount(c) == MAX_COUNT)
throw new Error("Maximum lock count exceeded");
if (compareAndSetState(c, c + SHARED_UNIT)) {
cachedHoldCounter = rh; // cache for release
rh.count++;
return 1;
}
}
}
我們來縷一縷上面的執(zhí)行步驟
- 如果是已經(jīng)有寫線程持有鎖均践,如果獲取鎖的線程不是當前線程,怎則返回摩幔,獲取失敗彤委。(讀寫鎖支持獲取寫鎖的線程再獲取讀鎖),否則或衡,進行2
- 如果讀線程請求鎖數(shù)量達到了65535(包括重入鎖)焦影,拋出error,否則進行3封断。
- 如果讀線程不用等待(也就是非公平鎖)斯辰,并且增加讀取鎖狀態(tài)數(shù)成功,那么就返回成功坡疼,否則進行4彬呻。
- 能到這一步說明CAS失敗,然后不斷循環(huán)知道CAS成功或者寫鎖被獲取
接下來我么看看如何釋放一個讀鎖
protected final boolean tryReleaseShared(int unused) {
HoldCounter rh = cachedHoldCounter;
Thread current = Thread.currentThread();
if (rh == null || rh.tid != current.getId())
rh = readHolds.get();
if (rh.tryDecrement() <= 0)
throw new IllegalMonitorStateException();
for (;;) {
int c = getState();
int nextc = c - SHARED_UNIT;
if (compareAndSetState(c, nextc))
return nextc == 0;
}
}
我們來分析一下這個釋放代碼柄瑰,HoldCounter是什么東西闸氮,AQS里從沒有見過。從名字里我們可以猜測教沾,這應該是計數(shù)器湖苞,還應該是持有讀鎖的線程的計數(shù)器。而我們再看如何獲得HoldCounter對象详囤。
rh = readHolds.get()
具體源碼就不貼了,readHolds的父類是ThreadLocal镐作,見到ThreadLocal可以將一個對象和線程綁定在一起藏姐,那這個對象一定是HoldCounter的示例了。這就和我們上面猜的差不多了该贾。HoldCounter綁定于一個線程羔杨,記錄的應該是當前線程持有共享鎖的數(shù)量。
接下來再看寫鎖的獲取和釋放杨蛋,應為寫鎖是獨占鎖兜材,這個和ReentrantLock比較相似,實現(xiàn)也比較簡單
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");
}
if ((w == 0 && writerShouldBlock(current)) || !compareAndSetState(c, c + acquires))
return false;
setExclusiveOwnerThread(current);
return true;
}
- 如果有線程獲取鎖(c 逞力!= 0)曙寡,并且沒有讀鎖(w==0即沒有寫鎖)或者不是當前線程持有寫鎖,則獲取鎖失敗寇荧,如果鎖重入大于上限举庶,拋出error
- 如果沒有線程獲取鎖(c == 0),并且當前線程需要阻塞那么就返回失斂铡户侥;如果增加寫線程數(shù)失敗也返回失敗镀琉。否則進行3。
- 設置獨占線程(寫線程)為當前線程蕊唐,返回true屋摔。
protected final boolean tryRelease(int releases) {
int nextc = getState() - releases;
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
if (exclusiveCount(nextc) == 0) {
setExclusiveOwnerThread(null);
setState(nextc);
return true;
} else {
setState(nextc);
return false;
}
}
static final int SHARED_SHIFT = 16;
static final int EXCLUSIVE_MASK = (1 << SHARED_SHIFT) - 1;
static int exclusiveCount(int c) {
return c & EXCLUSIVE_MASK;
}
這個方法讓狀態(tài)量與某一個常量相與,得到的就是后16的值替梨,也就是獲取的寫線程數(shù)