ReentrantReadWriteLock是一把读写锁,其基本实现依赖于两把锁,读锁和写锁,读锁之间能够进入,写锁之间互斥,这样对于锁的操作更加细粒度。首先要学习任何东西,都得了解他是用来干嘛的,其次知道他的流程,接下来就是真正的深入研究。下面放出一个我觉得不错的流程讲解:轻松掌握java读写锁(ReentrantReadWriteLock)的实现原理
1.构造器
public ReentrantReadWriteLock() {
//公平与否
//默认是不公平锁
this(false);
}
public ReentrantReadWriteLock(boolean fair) {
//总锁(真正的锁)
sync = fair ? new FairSync() : new NonfairSync();
//读锁,实现了lock接口
readerLock = new ReadLock(this);
//写锁,实现了lock接口
writerLock = new WriteLock(this);
}
//从这个地方就可以看出,其实只有一把锁,那就是sync,并且sync应该是支持两把锁的
//一把共享锁,一把是独占锁
2.有个东西不讲不行,那就是读写锁如何保存状态的?reentrantLock是利用一个state来说明锁的占用情况。那么sync也有一个变量state,他是利用了32位的int,高16位代表读锁,低16位代表写锁。
static final int SHARED_SHIFT = 16;
//读锁+1 的单位
static final int SHARED_UNIT = (1 << SHARED_SHIFT);
//重入的最大值65535
static final int MAX_COUNT = (1 << SHARED_SHIFT) - 1;
static final int EXCLUSIVE_MASK = (1 << SHARED_SHIFT) - 1;
//读锁的数量
static int sharedCount(int c) { return c >>> SHARED_SHIFT; }
//写锁的数量
static int exclusiveCount(int c) { return c & EXCLUSIVE_MASK; }
3.那么接下来看一下锁的操作。尝试了很多次从读锁开始下手,发现有点难度,就换了一个角度,那么我们就从写锁的操作开始入手,先走一个不竞争锁的过程,然后再分析有锁竞争的流程。writerLock.lock()
public void lock() {
sync.acquire(1);
}
//还是进入了aqs的方法中
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
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");
// Reentrant acquire
setState(c + acquires);
return true;
}
//写锁应不应该排队
//公平锁:需要查看是否有队列存在,若存在,则需要阻塞
//非公平锁:不需要阻塞
if (writerShouldBlock() ||
!compareAndSetState(c, c + acquires))
return false;
//占有独占锁
setExclusiveOwnerThread(current);
return true;
}
4.既然锁住了,那么就需要解锁了。writerLock.unlock()。
public void unlock() {
sync.release(1);
}
//其实一定会成功
public final boolean release(int arg) {
//尝试解锁
if (tryRelease(arg)) {
Node h = head;
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);
return true;
}
return false;
}
protected final boolean tryRelease(int releases) {
//判断 当前线程是不是占有锁的线程
if (!isHeldExclusively())
throw new IllegalMonitorStateException();
int nextc = getState() - releases;
boolean free = exclusiveCount(nextc) == 0;
if (free)
//将独占锁取消
setExclusiveOwnerThread(null);
setState(nextc);
return free;
}
5.假设现在考虑写锁需要排队了,之前存在一个写锁。
//第二把锁走到这一步
protected final boolean tryAcquire(int acquires) {
Thread current = Thread.currentThread();
//c!=0
int c = getState();
//w=1
int w = exclusiveCount(c);
if (c != 0) {
//就会走这一步
//(1)判断有没有写锁
//(2)判断是否是当前线程重入
//这个地方还有点复杂,得结合读锁来分析,先放一下。
if (w == 0 || current != getExclusiveOwnerThread())
return false;
//判断重入最大数 是否超过65535
if (w + exclusiveCount(acquires) > MAX_COUNT)
throw new Error("Maximum lock count exceeded");
//这个地方也代表了 state是重入数量
setState(c + acquires);
return true;
}
if (writerShouldBlock() ||
!compareAndSetState(c, c + acquires))
return false;
setExclusiveOwnerThread(current);
return true;
}
//接下来 又要到aqs里面了
private Node addWaiter(Node mode) {
Node node = new Node(Thread.currentThread(), mode);
// Try the fast path of enq; backup to full enq on failure
Node pred = tail;
//判断队列是否初始化,如果没有初始化,就enq(node)进行初始化
//如果初始化了,那么就通过cas放入链尾
if (pred != null) {
node.prev = pred;
if (compareAndSetTail(pred, node)) {
pred.next = node;
return node;
}
}
enq(node);
return node;
}
//剩下的就是和reentrantLock一样了
6.然后看一下读锁,读锁有点复杂了,涉及的东西也特别的多,因此还是得一步步地走下去。readLock.lock()
public void lock() {
//获取共享锁
sync.acquireShared(1);
}
public final void acquireShared(int arg) {
if (tryAcquireShared(arg) < 0)
doAcquireShared(arg);
}
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)) {
//没有其他读锁
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 1;
}
return fullTryAcquireShared(current);
}
//非公平锁readerShouldBlock最终进了这个方法
final boolean apparentlyFirstQueuedIsExclusive() {
Node h, s;
//其实就是判断队列老二是不是共享的
return (h = head) != null &&
(s = h.next) != null &&
!s.isShared() &&
s.thread != null;
}
7.那么读锁肯定也要涉及释放的。readLock.unlock()。
public void unlock() {
sync.releaseShared(1);
}
public final boolean releaseShared(int arg) {
//尝试释放
if (tryReleaseShared(arg)) {
doReleaseShared();
return true;
}
return false;
}
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;
}
//读锁还原 数量-1
for (;;) {
int c = getState();
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.
return nextc == 0;
}
}
-------------------------------------------------------------------------以后再补充了,太过于复杂了----------------------------------------------------