读写锁_ReentrantReadWriteLock原理
读写锁用的是同一个Sycn同步器,因此等待队列,state等也是同一个
场景:t1 线程 writeLock().lock()
t2线程readLock().lock()
, t3线程readLock().lock()
t4 线程 writeLock().lock()
1.t1成功上锁
流程与ReentrantLock加锁相比没有什么特殊之处,不同是写锁占了state的低16位,读锁使用的是state的高16位。
public void lock() {
sync.acquire(1);
}
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
tryAcquire:c!=0表示可能加了读锁或者写锁,w==0
:w是写锁,等于0表示加的是读锁,和t1互斥,直接false,反之执行 ||
后面 current != getExclusiveOwnerThread()
检查是否是自己重入,不是别人的写锁也是互斥,就false,w + exclusiveCount(acquires) > MAX_COUNT
表示重入超过16位最大数值(65535)则抛异常(几乎不会发生),最后 setState(c + acquires);
写锁计数加1。
如果c等于0就是没有开始加锁,writerShouldBlock()
看是否是公平锁或者非公平锁,非公平锁永远返回false,公平锁会检查队列,compareAndSetState
尝试将写锁改为1(CAS操作),成功就返回false,最后setExclusiveOwnerThread
,设置当前线程为owner线程
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(c);
if (c != 0) {
// (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;
}
if (writerShouldBlock() ||
!compareAndSetState(c, c + acquires))
return false;
setExclusiveOwnerThread(current);
return true;
}
2.t2尝试加锁
t1线程写锁加入成功, t2 线程执行readLock().lock()
读锁,进入sync.acquireShared(1)
流程,首先进入tryAcquireShared(1)
流程。如果有写锁占着,返回-1表示失败
public void lock() {
sync.acquireShared(1);
}
public final void acquireShared(int arg) {
if (tryAcquireShared(arg) < 0)
doAcquireShared(arg);
}
protected int tryAcquireShared(int arg) {
throw new UnsupportedOperationException();
}
tryAcquireShared
返回值:
- -1:表示失败
- 0:表示成功,但是后续节点不会去唤醒
- 正数表示成功,而且数值大小表示还有几个节点需要唤醒,读写锁返回1
tryAcquireShared流程:拿到当前线程和state状态,先判断有没有加写锁,t1是加过写锁的所以继续执行,getExclusiveOwnerThread() != current
判断加写锁的不是自己,不是自己条件成立就返回-1,是自己返回1。因为可能一个线程先加写锁后加读锁可以锁降级。因为t1加的是写锁,t2明显不是自己,就返回-1
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();//获得state状态
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);
}
acquireShared:在上一步acquireShared流程中tryAcquireShared(arg) < 0成立,进入
doAcquireShared`
public final void acquireShared(int arg) {
if (tryAcquireShared(arg) < 0)
doAcquireShared(arg);
}
doReleaseShared:首先将t2加入到节点中,此时还是活跃状态,还没有park,然后进入死循环,寻找当前线程节点的上一个节点,如果上一个节点是head,表示当前线程具有争抢的资格,tryAcquireShared
尝试争抢,失败就会进入 if (shouldParkAfterFailedAcquire(p, node)
流程,判断前驱是否是-1并将前驱节点设置-1,让前面的唤醒他,第一次返回false重新执行上面的方法,这样会继续去判断资格,尝试争抢,再次失败回到 shouldParkAfterFailedAcquire
中,这次成功就会 parkAndCheckInterrupt()
现在将线程parkz
private void doReleaseShared(int arg) {
final Node node = addWaiter(Node.SHARED);//加入共享类型的节点
boolean failed = true;
try {
boolean interrupted = false;
for (;;) {//死循环
final Node p = node.predecessor();//寻找t2的前驱节点
if (p == head) {//如果前驱节点是head,表示t2是老二,有资格争抢锁
int r = tryAcquireShared(arg);//所以尝试争抢锁,-1失败,1成功
if (r >= 0) {
setHeadAndPropagate(node, r);
p.next = null; // help GC
if (interrupted)
selfInterrupt();
failed = false;
return;
}
}
if (shouldParkAfterFailedAcquire(p, node) &&//判断前驱是否是-1并将前驱节点设置-1,让前面的唤醒他,第一次返回false重新执行上面的方法
parkAndCheckInterrupt())//现在将线程park
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
同理这样t3,t4进入node,不同的是因为t2,t3是读锁,所以是Shared
共享状态,t4写锁是Ex
独占状态
3.t1 unlock
unlock
:
public void unlock() {
sync.release(1);
}
**release
:tryRelease
**成功会将OwnerThread设为null,判断头节点是否是空,和是否不是0(判断有无后继节点),如果成立就会唤醒后继节点
public final boolean release(int arg) {
if (tryRelease(arg)) {
Node h = head;
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);
return true;
}
return false;
}
tryRelease
:state-1,查看exclusiveCount是否是0,如果不是0,表示这时一次锁的重入。如果减成0了,将OwnerThread设为null,返回true
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;
}
doAcquireShared
:**release
流程中unparkSuccessor
(h);会将后继节点唤醒,唤醒的地方在加锁时park住的地方:doAcquireShared
流程中的parkAndCheckInterrupt
处,唤醒后,进入死循环,判断是否是第二位,再进入tryAcquireShared
去竞争锁,加入锁成功,r = 1,setHeadAndPropagate
**将节点重新替换
private void doAcquireShared(int arg) {
final Node node = addWaiter(Node.SHARED);
boolean failed = true;
try {
boolean interrupted = false;
for (;;) {
final Node p = node.predecessor();
if (p == head) {
int r = tryAcquireShared(arg);
if (r >= 0) {
setHeadAndPropagate(node, r);
p.next = null; // help GC
if (interrupted)
selfInterrupt();
failed = false;
return;
}
}
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())//唤醒处
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
tryAcquireShared
:判断exclusiveCount个数是不为0,跳过,r是高16位读锁,compareAndSetState(c, c + SHARED_UNIT)
是让高16位加1,读锁加成功
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(c) != 0 &&
getExclusiveOwnerThread() != current)
return -1;
int r = sharedCount(c);
if (!readerShouldBlock() &&
r < MAX_COUNT &&
compareAndSetState(c, c + SHARED_UNIT)) {//让高16位加1
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);
}
setHeadAndPropagate
:setHead设置之前节点为头节点,node.next拿到当前节点下一个节点 t3,如果t3 isShared
是否是共享节点。条件满足,进入doReleaseShared
private void setHeadAndPropagate(Node node, int propagate) {
Node h = head; // Record old head for check below
setHead(node);
/*
* Try to signal next queued node if:
* Propagation was indicated by caller,
* or was recorded (as h.waitStatus either before
* or after setHead) by a previous operation
* (note: this uses sign-check of waitStatus because
* PROPAGATE status may transition to SIGNAL.)
* and
* The next node is waiting in shared mode,
* or we don't know, because it appears null
*
* The conservatism in both of these checks may cause
* unnecessary wake-ups, but only when there are multiple
* racing acquires/releases, so most need signals now or soon
* anyway.
*/
if (propagate > 0 || h == null || h.waitStatus < 0 ||
(h = head) == null || h.waitStatus < 0) {
Node s = node.next;
if (s == null || s.isShared())
doReleaseShared();
}
}
doReleaseShared:compareAndSetWaitStatus(h, Node.SIGNAL, 0)
把头节点状态由 -1 改成 0,成功后将头节点后继节点唤醒,也就是把t3唤醒,t3 也是和 t2一样阻塞在同一个地方,最后也到这一步,不满足isShared
。这样实现了读读并发
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;
}
}
4.t2 unlock,t3 unlock
public void unlock() {
sync.releaseShared(1);
}
releaseShared t2先执行,tryReleaseShared
将读锁计数-1,如果减为0才返回true,由于之前计数是二,releaseShared
流程不会执行下面,t3再次执行,就会将state置为0,且tryReleaseShared
流程是true,进入doReleaseShared
public final boolean releaseShared(int arg) {
if (tryReleaseShared(arg)) {
doReleaseShared();
return true;
}
return false;
}
tryReleaseShared 从下面死循环看,拿到state,减去高位的1,compareAndSetState
成功了,看state是否为0,是0才返回true
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;
}
}
doReleaseShared:ws == Node.SIGNAL
:头节点是否是-1,是的话compareAndSetWaitStatus
将头节点从-1改成0,成功后unparkSuccessor唤醒头节点的后继节点t4,t4是在 acquireQueued
中阻塞
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;
}
}
acquireQueued:看次节点是否是老二,tryAcquire尝试获取写锁,获取成功
final boolean acquireQueued(final Node node, int arg) {
boolean failed = true;
try {
boolean interrupted = false;
for (;;) {
final Node p = node.predecessor();
if (p == head && tryAcquire(arg)) {
setHead(node);
p.next = null; // help GC
failed = false;
return interrupted;
}
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())//暂停处
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}