簡介
ReentrantReadWriteLock,讀寫鎖。維護了一對相關的鎖,一個用於只讀操作,另一個用於寫入操作。只要沒有 writer,讀取鎖可以由多個 reader 線程同時保持。寫入鎖是獨佔的。
與互斥鎖相比,讀-寫鎖允許對共享數據進行更高級別的併發訪問。雖然一次只有一個線程(writer 線程)可以修改共享數據,但在許多情況下,任何數量的線程可以同時讀取共享數據(reader 線程)。當訪問讀寫比恰當的共享數據時,使用讀-寫鎖所允許的併發性將帶來更大的性能提高。
源碼分析
ReentrantReadWriteLock的實現方式是在內部定義了一個實現AbstractQueuedSynchronizer(詳見:JUC 源碼分析 - AbstractQueuedSynchronizer(AQS))的內部類Sync,Sync同時實現了AbstractQueuedSynchronizer中獨佔模式的獲取和釋放方法tryAcquire和tryRelease,和共享模式的獲取和釋放方法tryAcquireShared和tryReleaseShared,寫鎖WriteLock使用獨佔模式的方法控制鎖狀態,讀鎖ReadLock使用共享模式的方法控制鎖狀態,在WriteLock和ReadLock中使用同一個AQS的子類Sync,用AQS的status代表讀寫鎖的狀態計數,單個int值,通過位運算區分高低位,低16位代表寫狀態,高16位代表讀狀態。支持公平非公平實現,支持中斷,支持重入,支持鎖降級。
當併發讀寫時:
- 當有線程獲取了獨佔鎖,那麼後續所有其他線程的獨佔和共享鎖請求會加入同步隊列等待,後續當前線程的獨佔和共享鎖可以再次獲取;
- 當有線程獲取了共享鎖,那麼後續所有線程的獨佔鎖請求會加入同步隊列,後續所有線程的共享鎖請求可以繼續獲取鎖;
- 當獨佔鎖完全釋放時,會喚醒後繼節點,當喚醒的是共享節點時,會傳播向後喚醒後繼的共享節點;
- 當共享鎖完全釋放時,且當前沒有持有獨佔鎖,會喚醒後繼節點,當喚醒的是共享節點時,會傳播向後喚醒後繼的共享節點;
- 噹噹前線程已經獲取獨佔鎖,那麼當前線程可以繼續獲取共享鎖,當獨佔鎖退出時,鎖降級爲共享鎖;
- 一個線程可以同時進入多次共享鎖或獨佔鎖;
Sync
abstract static class Sync extends AbstractQueuedSynchronizer {
private static final long serialVersionUID = 6317671515068378041L;
static final int SHARED_SHIFT = 16;//32位分高16和低16位
static final int SHARED_UNIT = (1 << SHARED_SHIFT);//0000000000000001|0000000000000000
static final int MAX_COUNT = (1 << SHARED_SHIFT) - 1;//0000000000000000|1111111111111111
static final int EXCLUSIVE_MASK = (1 << SHARED_SHIFT) - 1;//0000000000000000|1111111111111111
static int sharedCount(int c) { return c >>> SHARED_SHIFT; }//獲取共享鎖計數,高16位
static int exclusiveCount(int c) { return c & EXCLUSIVE_MASK; }//獲取獨佔鎖計數,低16位
static final class HoldCounter {//每個線程持有的讀鎖計數。寫鎖獨佔所以寫鎖的低16位就代表獨佔線程重入計數,讀鎖共享,所以讀鎖的高16代表所有線程所有重入次數的計數,HoldCounter用ThreadLocal保存每個線程自己的重入計數。
int count = 0;
final long tid = Thread.currentThread().getId();
}
static final class ThreadLocalHoldCounter
extends ThreadLocal<HoldCounter> {
public HoldCounter initialValue() {//初始化
return new HoldCounter();
}
}
private transient ThreadLocalHoldCounter readHolds;//保存每個線程持有的讀鎖計數,不參與序列化。
private transient HoldCounter cachedHoldCounter;//緩存最新獲取共享鎖的線程的HoldCounter
private transient Thread firstReader = null;//緩存第一個獲取共享鎖的線程
private transient int firstReaderHoldCount;//緩存第一個獲取共享鎖的線程的重入計數
Sync() {
readHolds = new ThreadLocalHoldCounter();
setState(getState()); //用volatile的讀寫保證readHolds的可見性,保證readHolds對所有線程可見。
}
abstract boolean readerShouldBlock();//共享鎖獲取是否需要阻塞。控制共享模式的獲取鎖操作是否公平,子類實現。公平實現會看當前同步隊列是否有有效的等待節點,有則返回true,沒有則返回false,直接嘗試獲取鎖;非公平實現查看隊列中的下一個節點是否是獨佔模式,是獨佔模式,則需要阻塞,避免寫鎖的獲取總是發生飢餓,否則,可以直接嘗試獲取鎖。
abstract boolean writerShouldBlock();//獨佔鎖獲取是否需要阻塞。控制獨佔模式的獲取鎖操作是否公平,子類實現。公平實現會看當前同步隊列是否有有效的等待節點,有則返回true,需要加入同步隊列,順序獲取,沒有則返回false,直接嘗試獲取鎖;非公平實現返回false,代表不需要等待可以直接嘗試獲取鎖
protected final boolean tryRelease(int releases) {//獨佔模式的釋放鎖
if (!isHeldExclusively())//如果當前線程不是持有獨佔鎖的線程,將拋出異常
throw new IllegalMonitorStateException();
int nextc = getState() - releases;
boolean free = exclusiveCount(nextc) == 0;//exclusiveCount方法檢查釋放後的狀態的低16位,是0則獨佔鎖完全釋放,設置當前持有獨佔鎖的線程位null
if (free)
setExclusiveOwnerThread(null);
setState(nextc);//因爲是獨佔的釋放,所以直接set不會有問題。寫volatile,之前的所有內存操作會對所有線程可見,釋放之後其他線程才能獲取鎖。
return free;//完全釋放獨佔鎖才返回true,這裏有可能是鎖降級,或者是讀寫鎖完全釋放。
}
protected final boolean tryAcquire(int acquires) {//獨佔模式的獲取鎖
Thread current = Thread.currentThread();//獲取當前線程
int c = getState();//獲取當前狀態
int w = exclusiveCount(c);//獲取寫狀態
if (c != 0) {//狀態不是0,則當前鎖已被獲取
if (w == 0 || current != getExclusiveOwnerThread())//狀態不爲0,寫狀態爲0,說明讀狀態不爲0。即讀鎖被獲取或者當前線程不是持有獨佔鎖的線程,獲取獨佔鎖失敗,只有重入的獨佔鎖才能獲取
return false;
if (w + exclusiveCount(acquires) > MAX_COUNT)//寫狀態超出16位的最大值則失敗拋出異常
throw new Error("Maximum lock count exceeded");
setState(c + acquires);//執行到這裏說明狀態不爲0且寫狀態不爲0,說明獨佔鎖已經被獲取,且當前線程是持有獨佔鎖的線程,該獲取操作是重入獲取獨佔鎖
return true;
}
//當前狀態爲0
if (writerShouldBlock() //寫鎖是否需要等待,由子類實現,公平實現會看當前同步隊列是否有有效的等待節點,有則返回true,需要加入同步隊列,順序獲取,沒有則返回false,直接嘗試獲取鎖;非公平實現返回false,代表不需要等待可以直接嘗試獲取鎖
|| !compareAndSetState(c, c + acquires))//或CAS更新狀態失敗
return false;//不可以直接獲取或者CAS更新狀態失敗,返回false
setExclusiveOwnerThread(current);
return true;//可以獲取鎖且CAS更新狀態成功,設置當前線程爲持有獨佔鎖的線程,返回true
}
protected final boolean tryReleaseShared(int unused) {//共享模式的釋放鎖
Thread current = Thread.currentThread();
if (firstReader == current) {
if (firstReaderHoldCount == 1)
firstReader = null;
else
firstReaderHoldCount--;
} else {
HoldCounter rh = cachedHoldCounter;
if (rh == null || rh.tid != current.getId())
rh = readHolds.get();
int count = rh.count;
if (count <= 1) {
readHolds.remove();
if (count <= 0)
throw unmatchedUnlockException();
}
--rh.count;
}
for (;;) {
int c = getState();
int nextc = c - SHARED_UNIT;//狀態減去1左移16位的數,即讀狀態減一
if (compareAndSetState(c, nextc))//CAS更新讀狀態成功
return nextc == 0;//如果更新後的狀態位爲0,代表讀寫鎖完全釋放,返回true。返回true纔會喚醒同步隊列中的線程
}
}
private IllegalMonitorStateException unmatchedUnlockException() {
return new IllegalMonitorStateException(
"attempt to unlock read lock, not locked by current thread");
}
protected final int tryAcquireShared(int unused) {//共享模式的獲取鎖
Thread current = Thread.currentThread();
int c = getState();
if (exclusiveCount(c) != 0 &&
getExclusiveOwnerThread() != current)//獨佔鎖被持有,且當前線程不是持有獨佔鎖的線程,返回不可獲取
return -1;
int r = sharedCount(c);//讀狀態
if (!readerShouldBlock() &&
r < MAX_COUNT &&
compareAndSetState(c, c + SHARED_UNIT)) {//如果讀鎖不需要阻塞,且讀狀態小於最大值,且讀狀態CAS增加成功,意味着獲取共享鎖成功
if (r == 0) {//如果之前讀狀態是0,設置firstReader第一個讀線程爲當前線程,firstReaderHoldCount第一個讀線程獲取鎖計數1
firstReader = current;
firstReaderHoldCount = 1;
} else if (firstReader == current) {//firstReader第一個讀線程爲當前線程,說明這次是重入的獲取共享鎖,firstReaderHoldCount第一個讀線程獲取鎖計數+1
firstReaderHoldCount++;
} else {
HoldCounter rh = cachedHoldCounter;//緩存最後一個獲取共享鎖的線程狀態
if (rh == null || rh.tid != current.getId())
cachedHoldCounter = rh = readHolds.get();
else if (rh.count == 0)
readHolds.set(rh);
rh.count++;//readHolds保存每個線程持有的讀鎖計數
}
return 1;//獲取成功
}
//不可獲取
return fullTryAcquireShared(current);
}
final int fullTryAcquireShared(Thread current) {
HoldCounter rh = null;
for (;;) {
int c = getState();
if (exclusiveCount(c) != 0) {//獨佔鎖被持有
if (getExclusiveOwnerThread() != current)//當前線程不是持有獨佔鎖的線程。返回-1
return -1;
} else if (readerShouldBlock()) {//如果需要阻塞
if (firstReader == current) {
} else {
if (rh == null) {
rh = cachedHoldCounter;
if (rh == null || rh.tid != current.getId()) {
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)) {//CAS增加讀狀態成功
if (sharedCount(c) == 0) {//如果之前讀狀態是0,設置firstReader第一個讀線程爲當前線程,firstReaderHoldCount第一個讀線程獲取鎖計數1
firstReader = current;
firstReaderHoldCount = 1;
} else if (firstReader == current) {//firstReader第一個讀線程爲當前線程,說明這次是重入的獲取共享鎖,firstReaderHoldCount第一個讀線程獲取鎖計數+1
firstReaderHoldCount++;
} else {
if (rh == null)
rh = cachedHoldCounter;
if (rh == null || rh.tid != current.getId())
rh = readHolds.get();
else if (rh.count == 0)
readHolds.set(rh);
rh.count++;
cachedHoldCounter = rh; // cache for release
}
return 1;//獲取成功
}
}
}
final boolean tryWriteLock() {//僅當寫入鎖在調用期間未被另一個線程保持時獲取該鎖
Thread current = Thread.currentThread();
int c = getState();
if (c != 0) {//狀態不爲0
int w = exclusiveCount(c);
if (w == 0 || current != getExclusiveOwnerThread())//寫狀態爲0或者當前線程不是持有獨佔鎖的線程
return false;
if (w == MAX_COUNT)
throw new Error("Maximum lock count exceeded");
}
//寫狀態不爲0,且當前線程是持有獨佔鎖的線程
if (!compareAndSetState(c, c + 1))
return false;
setExclusiveOwnerThread(current);
return true;//CAS更新成功,設置當前線程爲持有獨佔鎖的線程,返回成功獲取
}
final boolean tryReadLock() {//僅當寫入鎖在調用期間未被另一個線程保持時獲取讀取鎖。
Thread current = Thread.currentThread();
for (;;) {
int c = getState();
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直到更新狀態成功,獲取到共享鎖
if (r == 0) {
firstReader = current;
firstReaderHoldCount = 1;
} else if (firstReader == current) {
firstReaderHoldCount++;
} else {
HoldCounter rh = cachedHoldCounter;
if (rh == null || rh.tid != current.getId())
cachedHoldCounter = rh = readHolds.get();
else if (rh.count == 0)
readHolds.set(rh);
rh.count++;
}
return true;
}
}
}
protected final boolean isHeldExclusively() {//當前線程是否是持有獨佔鎖的線程
return getExclusiveOwnerThread() == Thread.currentThread();
}
// Methods relayed to outer class
final ConditionObject newCondition() {//獨佔鎖可以獲取等待隊列
return new ConditionObject();
}
final Thread getOwner() {//獲取當前持有獨佔鎖的線程
return ((exclusiveCount(getState()) == 0) ?
null :
getExclusiveOwnerThread());
}
final int getReadLockCount() {//共享鎖計數
return sharedCount(getState());
}
final boolean isWriteLocked() {//獨佔鎖是否被持有
return exclusiveCount(getState()) != 0;
}
final int getWriteHoldCount() {//獲取當前線程持有的寫鎖計數
return isHeldExclusively() ? exclusiveCount(getState()) : 0;
}
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 == current.getId())
return rh.count;
int count = readHolds.get().count;
if (count == 0) readHolds.remove();
return count;
}
/**
* Reconstitute this lock instance from a stream
* @param s the stream
*/
private void readObject(java.io.ObjectInputStream s)
throws java.io.IOException, ClassNotFoundException {
s.defaultReadObject();
readHolds = new ThreadLocalHoldCounter();
setState(0); // reset to unlocked state
}
final int getCount() { return getState(); }//獲取總讀寫狀態
}
NonfairSync
static final class NonfairSync extends Sync {
private static final long serialVersionUID = -8159625535654395037L;
final boolean writerShouldBlock() {
return false; // writers can always barge
}
final boolean readerShouldBlock() {
/* As a heuristic to avoid indefinite writer starvation,
* block if the thread that momentarily appears to be head
* of queue, if one exists, is a waiting writer. This is
* only a probabilistic effect since a new reader will not
* block if there is a waiting writer behind other enabled
* readers that have not yet drained from the queue.
*/
return apparentlyFirstQueuedIsExclusive();
}
}
final boolean apparentlyFirstQueuedIsExclusive() {
Node h, s;
return (h = head) != null &&
(s = h.next) != null &&
!s.isShared() &&
s.thread != null;
}
FairSync
static final class FairSync extends Sync {
private static final long serialVersionUID = -2274990926593161451L;
final boolean writerShouldBlock() {
return hasQueuedPredecessors();
}
final boolean readerShouldBlock() {
return hasQueuedPredecessors();
}
}
public final boolean hasQueuedPredecessors() {
// The correctness of this depends on head being initialized
// before tail and on head.next being accurate if the current
// thread is first in queue.
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());
}
使用方式
public class ReadWriteLockTest {
private final Map<String, String> m = new TreeMap<String, String>();
private final ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
private final Lock r = rwl.readLock();
private final Lock w = rwl.writeLock();
public String get(String key) {
r.lock();
try {
return m.get(key);
} finally {
r.unlock();
}
}
public String put(String key, String value) {
w.lock();
try {
return m.put(key, value);
} finally {
w.unlock();
}
}
public void clear() {
w.lock();
try {
m.clear();
} finally {
w.unlock();
}
}
}