ReentrantReadWriteLock是Java中读写锁的一种实现,他可以让多个线程同时读取共享资源——读共享,写入的时候只允许一个线程独占——写独占.了解过ReentrantLock的应该知道这个是一个可重入锁,允许一个线程可以多次获取相同类型的锁.在读操作多于写操作的场景下,读写锁能够提高读操作的并发性,同时保证写操作的独占性.
读锁的使用
ReentrantReadWriteLock reentrantReadWriteLock = new ReentrantReadWriteLock();
ReadLock readLock = reentrantReadWriteLock.readLock();
try{
readLock.lock();
}catch(Exception e){
log.error("加锁失败!");
}finally{
readLock.unlock();
}
上篇文章已经对lock()加锁方法进行的分析,本篇文章主要对解锁进行分析
public void unlock() {
sync.releaseShared(1);
}
Sync是AbstractQueuedSynchronizer的子类,实现加锁和解锁主要是使用AQS来实现的.
public final boolean releaseShared(int arg) {
// 尝试释放锁,并且根据返回值来决定是否需要唤醒后续结点
if (tryReleaseShared(arg)) {
// 唤醒后续结点
doReleaseShared();
return true;
}
return false;
}
下面分别来看下上面两个关键方法
tryReleaseShared(arg)
protected final boolean tryReleaseShared(int unused) {
// 获取当前线程
Thread current = Thread.currentThread();
// 判断当前线程是不是当前获取读锁的第一个线程
if (firstReader == current) {
// 如果是第一个获取锁的线程,则判断有没有重入加锁过
if (firstReaderHoldCount == 1)
// firstReaderHoldCount == 1:说明只加过1次读锁
// 因此直接把 firstReader的记录清掉
firstReader = null;
else
// firstReaderHoldCount != 1:说明重入过读锁
// 将加锁次数减1
firstReaderHoldCount--;
} else {
// 如果不是第一个加锁的线程,则获取cachedHoldCounter缓存中的记录
HoldCounter rh = cachedHoldCounter;
if (rh == null || rh.tid != getThreadId(current))
// 如果cachedHoldCounter缓存是空的,或者缓存的不是当前线程
// 则需要从readHolds中来获取
rh = readHolds.get();
// count:
// == 1:说明只加过1次读锁
// == 0:说明没有加过锁,此时释放会导致报错
int count = rh.count;
if (count <= 1) {
// 因为只加过1次锁或压根没有加过锁,直接从readHolds中移除
readHolds.remove();
if (count <= 0)
throw unmatchedUnlockException();
}
// 加锁次数减1
--rh.count;
}
for (;;) {
// 获取state的值
int c = getState();
// 相当于state的高16位减1
int nextc = c - SHARED_UNIT;
if (compareAndSetState(c, nextc))
// 如果当前所有的读锁都被释放了,则返回
return nextc == 0;
}
}
doReleaseShared()
当上个方法最后return nextc == 0;返回true的时候,就会触发doReleaseShared()执行,意思是当所有的读锁和写锁都释放完毕.
private void doReleaseShared() {
for (;;) {
// 每次获取最新的head
Node h = head;
// head不是空,并且也不等于尾结点
if (h != null && h != tail) {
int ws = h.waitStatus;
if (ws == Node.SIGNAL) {
if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
continue;
// ws == Node.SIGNAL:后续节点等待激活状态
unparkSuccessor(h);
}
else if (ws == 0 &&
!compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
// 如果waitstatus为0(初始化状态),则将head的状态设置为PROPAGATE(代表共享等待模式,下一次获取共享锁无条件传播)
continue;
}
if (h == head)
// 当前头结点没有变化
break;
}
}
其实上一篇文章已经介绍了关于doReleaseShared(),这里就不再多说.