tryReleaseShared,尝试释放锁也就是改变state状态
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;
}
//开始释放读锁
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;
}
}
- int c = getState();获取state
- int nextc = c - SHARED_UNIT; 从高16位扣除掉读锁加锁次数;
- compareAndSetState(c, nextc) cas替换state变量
return nextc == 0; 读锁加锁次数都释放完了,nextc就是0,因此就返回true。如果没有释放完就得等外部在执行一次unlock操作了。
doReleaseShared,唤醒线程
private void doReleaseShared() {
/*
* Ensure that a release propagates, even if there are other
* in-progress acquires/releases. This proceeds in the usual
* way of trying to unparkSuccessor of head if it needs
* signal. But if it does not, status is set to PROPAGATE to
* ensure that upon release, propagation continues.
* Additionally, we must loop in case a new node is added
* while we are doing this. Also, unlike other uses of
* unparkSuccessor, we need to know if CAS to reset status
* fails, if so rechecking.
*/
for (;;) {
Node h = head;
if (h != null && h != tail) {
int ws = h.waitStatus;
if (ws == Node.SIGNAL) {
if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
continue; // loop to recheck cases
unparkSuccessor(h);
}
else if (ws == 0 &&
!compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
continue; // loop on failed CAS
}
if (h == head) // loop if head changed
break;
}
}
Node h = head;
- ws == Node.SIGNAL;这一步肯定是要进去的。
- !compareAndSetWaitStatus(h, Node.SIGNAL, 0);这一步假设cas成功了,即便不成功因为有for循环还是能设置成功的
- unparkSuccessor(h);按队列顺序释放线程,这个是所有唤醒线程的公用方法。