类关系图
🔒:读锁尝试获取锁。
/**
* Performs tryLock for read, enabling barging in both modes.
* This is identical in effect to tryAcquireShared except for
* lack of calls to readerShouldBlock.
*/
final boolean tryReadLock() {
Thread current = Thread.currentThread();
for (;;) {
int c = getState();
/**
* exclusiveCount(c) != 0 ----> 尝试获取读锁的时候,如果这个时候独占锁(写锁)的数量不为0。
* getExclusiveOwnerThread() != current ----> 当前持有锁的线程,与尝试获取锁的线程不是同一个线程的时候。
* 这两个判断在一起的意思就是:
* 当线程尝试获取读锁的时候首先要判断写锁是不是有线程持有,如果有那么需要判断尝试获取读锁的线程与持有写锁的线程是不是同一个线程。如果是那么可以继续获取读锁(这种情况叫做锁降级,在获取写锁的情况下再次获取读锁),如果不是那么不能获取读锁。
* 简单的说就是:如果有线程持有写锁,那么该线程之外的其他线程不能获取读锁。
*
*/
if (exclusiveCount(c) != 0 && getExclusiveOwnerThread() != current)
return false;
int r = sharedCount(c);
//不能超过共享锁(读锁)个数上限。
if (r == MAX_COUNT)
throw new Error("Maximum lock count exceeded");
/**
*CAS:更新读锁个数(+1)。因为高16位存的是读锁,所以c+SHARED_UNIT就相当于高16位加一。
*/
if (compareAndSetState(c, c + SHARED_UNIT)) {
if (r == 0) {
//firstReader第一个获取读锁的线程
firstReader = current;
firstReaderHoldCount = 1;
} else if (firstReader == current) {
firstReaderHoldCount++;
} else {
//每个线程持有的读锁的次数用的是ThreadLocal保存的
HoldCounter rh = cachedHoldCounter;
if (rh == null || rh.tid != getThreadId(current))
//cachedHoldCounter 最后一个获取读锁的线程,里面存的是线程id(tid)和线程持有读锁的次数(count)。
cachedHoldCounter = rh = readHolds.get();
else if (rh.count == 0)
readHolds.set(rh);
rh.count++;
}
return true;
}
}
}
写锁获取流程(NonFair)
private static ReadWriteLock lock = new ReentrantReadWriteLock();
private static Lock readLock = lock.readLock();
private static Lock writeLock = lock.writeLock();
private static Thread t = new Thread(new Runnable(){
@Override
public void run() {
System.out.println("writeLock start running!");
writeLock.lock();
writeLock.unlock();
System.out.println("writeLock ended!");
}
});
writerLock.lock() ----->
public void lock() {
sync.acquire(1);
}
sync.acquire(1) ------>
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
这里有两部分【tryAcquire(arg)】 and 【acquireQueued(addWaiter(Node.EXCLUSIVE), arg)】,这两部分分别是,首先尝试获取写锁,如果获取到了结束。如果没有获取到,那么加入队列。
第一部分【tryAcquire(arg)】 ----->【NonFair:不公平锁的体现】
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
/*
*这里没有用原子操作,我们来分析一下,首先能走到这一步的线程肯定只能是读线程,并且是持有写锁的线程,所以不会有其他获取写锁的线程发生竞争行为。那么读锁呢?前面我们已经分析了,如果此时存在写锁,那么是不能获取读锁的,所以这一步最多只能有一个线程再执行,就没必要再用CAS操作了。
*/
setState(c + acquires);
return true;
}
//通过源码我们知道writerShouldBlock()只有一行代码,returu false,所以不用考虑。当设置写锁数量成功的时候,那么当前线程就获取了写锁。
if (writerShouldBlock() ||
!compareAndSetState(c, c + acquires))
return false;
//获取写锁成功之后,设置独占线程。(当前持有写锁的线程)
setExclusiveOwnerThread(current);
return true;
}
第二部分 ----->acquireQueued(addWaiter(Node.EXCLUSIVE), arg) ,当第一步获取写锁失败之后,这个时候要加入Node队列。第二部分中包括【addWaiter(Node.EXCLUSIVE)】
addWaiter(Node.EXCLUSIVE) ------>创建需要获取锁的线程节点
/*
*注意Node的头节点是空的,里面没有线程。
*/
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;
//当队列不为空的时候
if (pred != null) {
node.prev = pred;
if (compareAndSetTail(pred, node)) {
pred.next = node;
return node;
}
}
//当队列为空的时候
enq(node);
return node;
}
接下来 ----->
final boolean acquireQueued(final Node node, int arg) {
boolean failed = true;
try {
//这里是用于控制执行完之后是否需要给当前线程设置中断标识。也就是控制是否执行这句代码selfInterrupt();
boolean interrupted = false;
for (;;) {
final Node p = node.predecessor();
//如果当前节点正好在头结点下面的第一个节点,那么在尝试获取一次锁。【NonFair:不公平锁的体现】
if (p == head && tryAcquire(arg)) {
setHead(node);
p.next = null; // help GC
failed = false;
return interrupted;
}
//解析shouldParkAfterFailedAcquire----->【a】
//解析parkAndCheckInterrupt() ------->【b】
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
//在什么情况下会进入这里,暂时还没想明白。//TODO:
cancelAcquire(node);
}
}
【a】 shouldParkAfterFailedAcquire(p,node)------>当线程获取锁失败的时候,就需要把当前线程Park掉等待释放锁之后再按顺序将其唤醒。此时需要把前置节点设置为SIGNAL(-1)
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
int ws = pred.waitStatus;
//如果前置节点已经是SIGNAL直接返回
if (ws == Node.SIGNAL)
/*
* This node has already set status asking a release
* to signal it, so it can safely park.
*/
return true;
//如果waitStatus大于0(也就是CANCELLED)这个时候,需要将其跳过,也就是从Node中移除该节点,然后返回false,这个时候前置节点还不知道是不是(SIGNAL),所以该方法的外层循环还会继续,这个时候又会调用一次tryAcquire(arg).
if (ws > 0) {
/*
* Predecessor was cancelled. Skip over predecessors and
* indicate retry.
*/
do {
node.prev = pred = pred.prev;
} while (pred.waitStatus > 0);
pred.next = node;
} else {
/*
* waitStatus must be 0 or PROPAGATE. Indicate that we
* need a signal, but don't park yet. Caller will need to
* retry to make sure it cannot acquire before parking.
* waitStatus只能是0或者-3。标明我们需要一个SIGNAL(-1)信号,但不是中断线程。调用者需要再次尝试获取锁,确保在中断线程之前不能获取到锁。
* waitStatus只能是 0 或者 -3,因为CANCELLED(1)已经被跳过去了。(但是CONDITION:-2为什么没有,暂时还没涉及,后续会补上.//TODO:)
*/
compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
}
return false;
}
【b】 parkAndCheckInterrupt() ------> 中断线程
private final boolean parkAndCheckInterrupt() {
//当线程获取不到锁之后就回中断自己,不再获取锁了,等待释放锁之后将其唤醒
LockSupport.park(this);
//被唤醒之后继续执行下面一行代码,如果这个时候线程是被中断唤醒的,那么要重新设置一下该线程的中断状态,LockSupport.park也能响应中断状态,但是如果不返回当前线程的中断状态我们就没办法知道,所以这里返回的事当前线程的中断状态,但是该方法返回中断状态之后会清楚中断标志,所以这个时候外层循环中的boolean interrupted = false;这个参数就有作用了,这个参数就是记录并返回当前线程的中断状态,从而可以重新设置当前线程的中断状态。
return Thread.interrupted();
}
至此,写锁的获取就算完事了。接下来我们来分析,写锁的释放。
写锁的释放(未完待续。。。。)
public void unlock() {
sync.release(1);
}
public final boolean release(int arg) {
//释放锁 ----【a】
if (tryRelease(arg)) {
Node h = head;
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);
return true;
}
return false;
}
AQS condition
public final void await() throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
//添加需要等待的wait节点----->【Condition-a】
Node node = addConditionWaiter();
int savedState = fullyRelease(node);
int interruptMode = 0;
while (!isOnSyncQueue(node)) {
LockSupport.park(this);
if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
break;
}
if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
interruptMode = REINTERRUPT;
if (node.nextWaiter != null) // clean up if cancelled
unlinkCancelledWaiters();
if (interruptMode != 0)
reportInterruptAfterWait(interruptMode);
}
【Condition-a】 ------>解析。
private Node addConditionWaiter() {
Node t = lastWaiter;
// If lastWaiter is cancelled, clean out.
if (t != null && t.waitStatus != Node.CONDITION) {
//添加wait的时候如果最后一个节点不为CONDITION就回遍历整个队列把所有状态不为CONDITION的全部移除掉 ----->【Condition-b】
unlinkCancelledWaiters();
t = lastWaiter;
}
Node node = new Node(Thread.currentThread(), Node.CONDITION);
if (t == null)
firstWaiter = node;
else
t.nextWaiter = node;
lastWaiter = node;
return node;
}
【Condition-b】----->分析。移除已经取消的节点的时候。思路:里面涉及三个节点位置关系分别为【trail】--->【t】---->【next】。理解的时候可以按照这个思路来理解
private void unlinkCancelledWaiters() {
Node t = firstWaiter;
Node trail = null;
while (t != null) {
Node next = t.nextWaiter;
if (t.waitStatus != Node.CONDITION) {
t.nextWaiter = null;
if (trail == null)
firstWaiter = next;
else
trail.nextWaiter = next;
if (next == null)
lastWaiter = trail;
}
else
trail = t;
t = next;
}
}