java并发AQS原理之ReentrantLock
看本文建议结合源码
首先来看看ReentrantLock的构造方法,它的构造方法有两个,如所示:
public ReentrantLock() {
sync = new NonfairSync();
}
public ReentrantLock(booleanfair) {
sync = fair ? new FairSync() : new NonfairSync();
}
在ReentrantLock中定义了三个内部类,分别是Sync以及Sync的子类FairSync、NonfairSync,分别实现了公平策略和非公平策略,默认实现的是非公平策略。
公平和非公平的第一个区分点,首先来看NonfairSync的lock方法
finalvoid lock() {
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
}
假设有三个并发的线程,第一个线程调用lock的时候,compareAndSetState(0,1)是可以正常执行的返回true,然后把当前线程置为独占模式的当前所有者,然后该线程可以执行后续操作。如果第二个线程被放到队列中,第三个线程走到compareAndSetState(0,1),第一个线程刚好unlock掉,那么第三个线程就会直接执行,而不是等待第二个线程执行后,这也就是非公平模式的特点。第二个线程调用lock的时候,compareAndSetState(0,1)返回false,执行acquire(1),该方法会调用tryAcquire(1)和acquireQueued(addWaiter(Node.EXCLUSIVE), 1)
先来看下tryAcquire(1),该方法的实现是nonfairTryAcquire方法,代码如下:
finalboolean nonfairTryAcquire(intacquires) {
final Thread current = Thread.currentThread();
intc = getState();
if (c == 0) {
if (compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
returntrue;
}
}
elseif (current == getExclusiveOwnerThread()) {
intnextc = c + acquires;
if (nextc < 0) // overflow
thrownew Error("Maximum lock count exceeded");
setState(nextc);
returntrue;
}
returnfalse;
}
看代码,结合三个线程并发的场景,第一个线程lock后,会把state(默认值是0)的值置为1,所以c的值现在是1,getExclusiveOwnerThread()是第一个线程,所以直接返回false,执行acquireQueued(addWaiter(Node.EXCLUSIVE), 1),方法addWaiter(Node.EXCLUSIVE)的作用是根据给的排他模式把当前线程(也就是第二个线程)加入队列中,此时的队列情况如下:
首次有线程(Node)进入队列时,会先new一个Node对象作为头部,然后把首次进队列的线程(Node)挂在其后面。Node对象是包含当前线程和模型的作为队列元素
然后执行acquireQueued(final Node node, int arg),来看下代码:
finalboolean acquireQueued(final Node node, intarg) {
booleanfailed = true;
try {
booleaninterrupted = false;
for (;;) {
final Node p = node.predecessor();
if (p == head && tryAcquire(arg)) {
setHead(node);
p.next = null; // help GC
failed = false;
returninterrupted;
}
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
第二个线程入队列,前面是一个初始化的对象也是head的值,进入循环后,会再次tryAcquire(1),假设依然获取不到,则进入第二个if语句中,执行shouldParkAfterFailedAcquire(p, node)方法和parkAndCheckInterrupt()方法,parkAndCheckInterrupt()就会把第二个线程阻塞起来。
公平和非公平的第二个区分点在tryAcquire(int acquires)方法中,如下:
protectedfinalboolean tryAcquire(intacquires) {
final Thread current = Thread.currentThread();
intc = getState();
if (c == 0) {
if (!hasQueuedPredecessors() &&
compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
returntrue;
}
}
elseif (current == getExclusiveOwnerThread()) {
intnextc = c + acquires;
if (nextc < 0)
thrownew Error("Maximum lock count exceeded");
setState(nextc);
returntrue;
}
returnfalse;
}
当第一个线程走完,第二个线程在队列中,第三个线程调用acquire(int arg)时先调用tryAcquire方法,会进行!hasQueuedPredecessors() && compareAndSetState(0, acquires)判断,而非公平的只有进行了compareAndSetState(0, acquires)判断,不考虑队列是否有等待的线程,而hasQueuedPredecessors()该方法就是判断队列中是否有阻塞的线程,如果有返回true,然后执行acquireQueued方法入队列。在第三个线程入队列同时,第一个线程走完调用unlock操作,该操作就会去调用release方法,如下
publicfinalboolean release(intarg) {
if (tryRelease(arg)) {
Node h = head;
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);
returntrue;
}
returnfalse;
}
最终调用unparkSuccessor(h)方法,
privatevoid unparkSuccessor(Node node) {
intws = node.waitStatus;
if (ws < 0)
compareAndSetWaitStatus(node, ws, 0);
Node s = node.next;
if (s == null || s.waitStatus > 0) {
s = null;
for (Node t = tail; t != null && t != node; t = t.prev)
if (t.waitStatus <= 0)
s = t;
}
if (s != null)
LockSupport.unpark(s.thread);
}
从队列中获取第二个线程,unpark操作。阻塞结束后还在acquireQueued(final Node node, intarg)的循环体中,此时!hasQueuedPredecessors()和compareAndSetState(0, acquires)都为true,并把当前线程置为独占模式的当前所有者,然后该线程可以执行程序中lock()方法的后续操作。