ReentrantReadWriteLock解读
-
读写锁简介
有的时候对某些资源,读取次数远大于更新次数,但同时写也是必不可少的,这时候我我们可以考虑用读写锁 -
类图
-
主要类成员属性
//提供读锁的内部类
/** Inner class providing readlock */
private final ReentrantReadWriteLock.ReadLock readerLock;
//提供写锁的内部类
/** Inner class providing writelock */
private final ReentrantReadWriteLock.WriteLock writerLock;
//实现了AbstractQueuedSynchronizer抽象类,提供锁操作的同步机制
/** Performs all synchronization mechanics */
final Sync sync;
/**
* Synchronization implementation for ReentrantReadWriteLock.
* Subclassed into fair and nonfair versions.
* 为ReentrantReadWriteLock实现的抽象同步器,该类的子类分为公平和分公平版本
*/
abstract static class Sync extends AbstractQueuedSynchronizer {
private static final long serialVersionUID = 6317671515068378041L;
/*
* Read vs write count extraction constants and functions.
* Lock state is logically divided into two unsigned shorts:
* The lower one representing the exclusive (writer) lock hold count,
* and the upper the shared (reader) hold count.
*下面会介绍提取读写锁的常量和和函数,锁状态也既就是AQS里的state变量,被拆成两部分,
* int类型共32位二进制码, 低16位代表独占锁,高16位代表共享锁
*/
//区分共享锁的位移量
static final int SHARED_SHIFT = 16;
//共享锁的基本操作单元
static final int SHARED_UNIT = (1 << SHARED_SHIFT);
//最大数量的读或者写锁,当数量达到最大值,再次请求会抛出异常Maximum lock count exceeded
static final int MAX_COUNT = (1 << SHARED_SHIFT) - 1;
//锁状态与上独占锁模就获取到独占锁数量
static final int EXCLUSIVE_MASK = (1 << SHARED_SHIFT) - 1;
/** Returns the number of shared holds represented in count
* 把锁状态state 左移16位得到共享锁数量
* */
static int sharedCount(int c) { return c >>> SHARED_SHIFT; }
/** Returns the number of exclusive holds represented in count
* 锁状态与上独占锁模就获取到独占锁数量
* */
static int exclusiveCount(int c) { return c & EXCLUSIVE_MASK; }
-
线程读锁持有器ThreadLocalHoldCounter
/**- A counter for per-thread read hold counts.
- Maintained as a ThreadLocal; cached in cachedHoldCounter
- 为每个线程记录读锁数量的计数器
*/
static final class HoldCounter {
int count = 0;
// Use id, not reference, to avoid garbage retention
final long tid = getThreadId(Thread.currentThread());
}
/**
- ThreadLocal subclass. Easiest to explicitly define for sake
- of deserialization mechanics.
- 把ThreadLocalHoldCounter把HoldCounter类封装为线程隔离的变量
*/
static final class ThreadLocalHoldCounter
extends ThreadLocal {
public HoldCounter initialValue() {
return new HoldCounter();
}
}
/**
- The number of reentrant read locks held by current thread.
- Initialized only in constructor and readObject.
- Removed whenever a thread’s read hold count drops to 0.
- 当前线程持有的读锁数量
*/
private transient ThreadLocalHoldCounter readHolds;
-
tryRelease方法,尝试释放写锁
//重写AQS的tryRelease,释放独占锁
protected final boolean tryRelease(int releases) {
if (!isHeldExclusively())
throw new IllegalMonitorStateException();
//减去需要释放的数值
int nextc = getState() - releases;
//因为是重入锁,所以释放以后需要判断该线程是否完全释放了独占锁
boolean free = exclusiveCount(nextc) == 0;
//如果释放完成,设置当前独占锁线程为null
if (free)
setExclusiveOwnerThread(null);
//因为持有写所,所以对state的操作是线程安全的,不需要额外的同步
setState(nextc);
return free;
} -
tryAcquire尝试获取一定数量的写锁
//尝试获取写锁
protected final boolean tryAcquire(int acquires) {
/*
* Walkthrough:
* 1. If read count nonzero or write count nonzero
* and owner is a different thread, fail.
* 2. If count would saturate, fail. (This can only
* happen if count is already nonzero.)
* 3. Otherwise, this thread is eligible for lock if
* it is either a reentrant acquire or
* queue policy allows it. If so, update state
* and set owner.
*/
Thread current = Thread.currentThread();
int c = getState();
int w = exclusiveCount©;
//已经有读锁或者写锁
if (c != 0) {
//如果有其他线程的读锁,或者其他线程持有写锁,返回false
// (Note: if c != 0 and w == 0 then shared count != 0)
if (w == 0 || current != getExclusiveOwnerThread())
return false;
//判断同一线程的写锁是否超过最大值。超过最大值抛出异常
if (w + exclusiveCount(acquires) > MAX_COUNT)
throw new Error(“Maximum lock count exceeded”);
//同一线程锁重入,无需额外同步
// Reentrant acquire
setState(c + acquires);
return true;
}
//读锁和写锁都没有被获取,如果是非公平锁writerShouldBlock返回false
//然后看cas操作是否成功,如果成功则获取到写锁,如果是公平锁并且线程队列中已经有
//线程在排队了,则返回false
if (writerShouldBlock() ||
!compareAndSetState(c, c + acquires))
return false;
//获取成功,直接设置当前线程读写锁的值
setExclusiveOwnerThread(current);
return true;
}//尝试获取写锁
protected final boolean tryAcquire(int acquires) {
/*
* Walkthrough:
* 1. If read count nonzero or write count nonzero
* and owner is a different thread, fail.
* 2. If count would saturate, fail. (This can only
* happen if count is already nonzero.)
* 3. Otherwise, this thread is eligible for lock if
* it is either a reentrant acquire or
* queue policy allows it. If so, update state
* and set owner.
*/
Thread current = Thread.currentThread();
int c = getState();
int w = exclusiveCount©;
//已经有读锁或者写锁
if (c != 0) {
//如果有其他线程的读锁,或者其他线程持有写锁,返回false
// (Note: if c != 0 and w == 0 then shared count != 0)
if (w == 0 || current != getExclusiveOwnerThread())
return false;
//判断同一线程的写锁是否超过最大值。超过最大值抛出异常
if (w + exclusiveCount(acquires) > MAX_COUNT)
throw new Error(“Maximum lock count exceeded”);
//同一线程锁重入,无需额外同步
// Reentrant acquire
setState(c + acquires);
return true;
}
//读锁和写锁都没有被获取,如果是非公平锁writerShouldBlock返回false
//然后看cas操作是否成功,如果成功则获取到写锁,如果是公平锁并且线 程队列中已经有
//线程在排队了,则返回false
if (writerShouldBlock() ||
!compareAndSetState(c, c + acquires))
return false;
//获取成功,直接设置当前线程读写锁的值
setExclusiveOwnerThread(current);
return true;
} -
tryReleaseShared尝试释放读锁
//尝试释放共享锁
protected final boolean tryReleaseShared(int unused) {
Thread current = Thread.currentThread();
//如果请求释放锁的线程是第一个获取读锁的线程.否则看下是否是最近刚获取了读锁的线程
if (firstReader == current) {
//如果只获取了一次读锁,直接释放,否则减1
// 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;
//小于等于1直接从线程变量中把HoldCounter对象移除
if (count <= 1) {
readHolds.remove();
//如果小于等于0,出现了异常情况,需要抛出异常
if (count <= 0)
throw unmatchedUnlockException();
}
//释放读锁
–rh.count;
}
//释放掉当前线程的持有的一个读锁之后,需要用更改state,这也是为何需要for循环的原因
for (;? {
int c = getState();
//减去state变量代表的读锁单元
int nextc = c - SHARED_UNIT;
if (compareAndSetState(c, nextc))
// Releasing the read lock has no effect on readers,
// but it may allow waiting writers to proceed if
// both read and write locks are now free.
//当读写锁都没有的时候返回true
return nextc == 0;
}
} -
tryAcquireShared 尝试获取共享锁
//尝试获取共享锁
protected final int tryAcquireShared(int unused) {
/*
* Walkthrough:
* 1. If write lock held by another thread, fail.
* 2. Otherwise, this thread is eligible for
* lock wrt state, so ask if it should block
* because of queue policy. If not, try
* to grant by CASing state and updating count.
* Note that step does not check for reentrant
* acquires, which is postponed to full version
* to avoid having to check hold count in
* the more typical non-reentrant case.
* 3. If step 2 fails either because thread
* apparently not eligible or CAS fails or count
* saturated, chain to version with full retry loop.
/
Thread current = Thread.currentThread();
int c = getState();
//如果其他线程持有写锁,直接返回失败
if (exclusiveCount© != 0 &&
getExclusiveOwnerThread() != current)
return -1;
int r = sharedCount©;
//读锁不应该阻塞,并且读锁没有到底最大值,并且通过了cas更新锁状态,加上读锁单元
if (!readerShouldBlock() &&
r < MAX_COUNT &&
compareAndSetState(c, c + SHARED_UNIT)) {
//如果读锁等于o,则对第一个持有读锁信息的变量赋值,如firstReader,firstReaderHoldCount
if (r == 0) {
firstReader = current;
firstReaderHoldCount = 1;
} else if (firstReader == current) { //当前线程的读锁加1
firstReaderHoldCount++;
} else {
//判断当前的线程是不是最新获取读锁的线程,如果不是再获取当前线程的本地变量HoldCounter
HoldCounter rh = cachedHoldCounter;
if (rh == null || rh.tid != getThreadId(current))
cachedHoldCounter = rh = readHolds.get();
else if (rh.count == 0)
readHolds.set(rh);
rh.count++; //线程所占读锁加1
}
return 1;
}
/*
* Full version of acquire for reads, that handles CAS misses
* and reentrant reads not dealt with in tryAcquireShared.
*/
//处理cas操作失败,或者读锁被阻塞了,应该调用下面的方法进行读锁获取
return fullTryAcquireShared(current);
} -
fullTryAcquireShared方法是对tryAcquireShared的补充,实际上方法体里的重要内容和上面的方法差不多,这儿就不写注释了。通过无线循环来获取锁
/**
* Full version of acquire for reads, that handles CAS misses
* and reentrant reads not dealt with in tryAcquireShared.
/
final int fullTryAcquireShared(Thread current) {
/
* This code is in part redundant with that in
* tryAcquireShared but is simpler overall by not
* complicating tryAcquireShared with interactions between
* retries and lazily reading hold counts.
*/
//用于获取线程局部变量
HoldCounter rh = null;
for (;? {
int c = getState();
if (exclusiveCount© != 0) {
if (getExclusiveOwnerThread() != current)
return -1;
// else we hold the exclusive lock; blocking here
// would cause deadlock.
} else if (readerShouldBlock()) {
// Make sure we’re not acquiring read lock reentrantly
//如果获取到读锁的是当前线程,firstReaderHoldCount应该是大于0的
if (firstReader == current) {
// assert firstReaderHoldCount > 0;
} else {
if (rh == null) {
rh = cachedHoldCounter;
if (rh == null || rh.tid != getThreadId(current)) {
rh = readHolds.get();
if (rh.count == 0)
readHolds.remove();
}
}
if (rh.count == 0)
return -1;
}
}
if (sharedCount© == MAX_COUNT)
throw new Error(“Maximum lock count exceeded”);
if (compareAndSetState(c, c + SHARED_UNIT)) {
if (sharedCount© == 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;
}
}
} -
tryWriteLock尝试获取独占锁
/**
* Performs tryLock for write, enabling barging in both modes.
* This is identical in effect to tryAcquire except for lack
* of calls to writerShouldBlock.
*/
//尝试获取写锁
final boolean tryWriteLock() {
Thread current = Thread.currentThread();
int c = getState();
//如果获取锁状态不为0,则走if语句体,进行详细判断
if (c != 0) {
int w = exclusiveCount(c);
//第一中情况有其他线程还在读数据,不能更改,所以不能或者到读锁
//第二种情况是有其他线程已经获取了写锁,即独占锁
if (w == 0 || current != getExclusiveOwnerThread())
return false; //满足上面两种情况之一返回false
//写锁重入已是最大值,不可再获取。抛出异常
if (w == MAX_COUNT)
throw new Error("Maximum lock count exceeded");
}
//cas失败证明锁状态已经被其他线程修改
if (!compareAndSetState(c, c + 1))
return false;
//设置获取到独占锁的线程
setExclusiveOwnerThread(current);
return true;
}
- getReadHoldCount()查询当前线程的重入锁数量
//查询当前线程的重入锁数量
final int getReadHoldCount() {
//如果读锁数量为0,那肯定当前线程的读锁肯定为0
if (getReadLockCount() == 0)
return 0;
//如果当前线程是第一个获取读锁的线程,则返回其占有的读锁数量
Thread current = Thread.currentThread();
if (firstReader == current)
return firstReaderHoldCount;
//如果不是最近获取读锁的线程,则从线程局部变量里取 HoldCounter
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;
}
- 静态内部类写锁WriteLock
/**
* The lock returned by method {@link ReentrantReadWriteLock#writeLock}.
*/
public static class WriteLock implements Lock, java.io.Serializable {
private static final long serialVersionUID = -4992448646407690164L;
private final Sync sync;
/**
* Constructor for use by subclasses
*
* @param lock the outer lock object
* @throws NullPointerException if the lock is null
*/
protected WriteLock(ReentrantReadWriteLock lock) {
sync = lock.sync;
}
加锁和释放锁都是调用AQS(AbstractQueuedSynchronizer)子类来实现的
public void lock() {
sync.acquire(1);
}
public void unlock() {
sync.release(1);
}
静态内部类ReadLock和WriteLock 是相同的道理,具体同步实现都是由AQS的子类实现