代码
public class Test {
public static void main(String[] args) {
ReentrantLock lock = new ReentrantLock();
new Thread(()->{
lock.lock();
try {
// ThreadA执行业务代码
} finally {
lock.unlock();
}
},"ThreadA").start();
new Thread(()->{
lock.lock();
try {
// ThreadB执行业务代码
} finally {
lock.unlock();
}
},"ThreadB").start();
new Thread(()->{
lock.lock();
try {
// ThreadC执行业务代码
} finally {
lock.unlock();
}
},"ThreadC").start();
}
}
分析
我们首先看lock()方法,具体代码如下:
ReentrantLock.java:
public void lock() {
sync.lock();
}
我们点击sync后面的lock,进入Sync内部类中的lock()方法中,代码如下:
ReentrantLock.java:
abstract static class Sync extends AbstractQueuedSynchronizer {
……
abstract void lock();
可以看出lock()是一个抽象方法,因此我们看该方法的重写方法,如下:
我们本次选择NofairSync非公平类中的lock()方法进行分析,代码如下:
ReentrantLock.java:
static final class NonfairSync extends Sync {
……
final void lock() {
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
}
可以看到compareAndSetState(0, 1)方法,点击该方法进入,代码如下:
AbstractQueuedSynchronizer.java:
protected final boolean compareAndSetState(int expect, int update) {
return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
}
该方法的作用就是比较state的值是不是0,如果是0就将该值更新为1,我们来看state变量,如下:
AbstractQueuedSynchronizer.java:
private volatile int state;
该变量没有被显示赋值,所以初始值就是0,通过分析ReentrantLock类中的代码,可以发现:
lock()和unlock()方法都和Sync内部类有关,以及NonfairSync和FairSync都是Sync内部类的实现类,其实AbstractQueuedSynchronizer.java才是锁的根本,所以在看代码之前,我们先看一下AbstractQueuedSynchronizer.java类的结构吧,该类主要结构如下:
private volatile int state;
private transient volatile Node head;
private transient volatile Node tail;
private transient Thread exclusiveOwnerThread;(从父类AbstractOwnableSynchronizer继承而来)
然后该类中有一个内部类Node,Node内部类中有几种状态,还有prev和next,说明这是一个双向链表的节点,里面还有一个Thread,说明该节点中存储的是线程,根据分析可以知道AbstractQueuedSynchronizer类的图形描述如下:
我们通过顾客到银行办理业务(三个顾客来银行办理业务,但是只能有一个人在业务窗口办理业务,其他人只能在候客区等待,当业务)的例子来对AQS进行分析;
1、假设让ThreadA线程来办理业务,现在state就是0,我们回到ReentrantLock类中内部类NonfairSync类中的lock()方法,我们已经完成了compareAndSetState(0, 1)方法,返回结果是true,那么此时state变成了1,然后程序继续向下执行,就到了setExclusiveOwnerThread(Thread.currentThread()),我们点进该方法,代码如下:
AbstractOwnableSynchronizer.java:
private transient Thread exclusiveOwnerThread;
protected final void setExclusiveOwnerThread(Thread thread) {
exclusiveOwnerThread = thread;
}
这个操作相当于让下图中银行受理业务窗口位置的Thread变成了ThreadA,到此为止,ThreadA线程的使命就完成了,图形就变成了这个样子:
2、现在让ThreadB线程来办理业务,前面的分析过程几乎和ThreadA线程的分析过程一致,我们直接看NofairSync非公平类中的lock()方法,代码如下:
ReentrantLock.java:
static final class NonfairSync extends Sync {
……
final void lock() {
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
}
由于现在state是1,所以compareAndSetState(0, 1)是false,该方法具体代码在ThreadA线程中已经看过了,这里不再分析,我们看else中的acquire(1),点击acquire之后代码如下:
AbstractQueuedSynchronizer.java:
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
注意arg是1,里面涉及到多个方法,我们先看tryAcquire(arg) ,点击该方法进去,代码如下:
AbstractQueuedSynchronizer.java:
protected boolean tryAcquire(int arg) {
throw new UnsupportedOperationException();
}
该方法将被子类实现,我们看一下到底实现该方法的子类,如下:
我们本次分析的是非公平锁,所以选择NonfairSync,tryAcquire()实现方法如下:
ReentrantLock.java类中的静态内部类NonfairSync.java:
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
注意acquires是1,我们点击nonfairTryAcquire方法,代码如下:
ReentrantLock.java中的静态抽象内部类Sync.java:
final boolean nonfairTryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
if (compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
if (nextc < 0) // overflow
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
注意上面的acquires是1,我们一行一行代码来看,current后面的当前线程是ThreadB线程,c获取到的状态是1(getState()方法的作用就是返回state),所以c不等于0,因此在看else if中的判断条件,上面已经说过current代表ThreadB线程,然后看getExclusiveOwnerThread()方法,该方法返回的就是字段exclusiveOwnerThread的值,该值是ThreadA线程(在上面分析ThreadA线程的时候提到的),所以两个值并不相等,因此else if也不满足,因此nonfairTryAcquire()方法最终返回false,那么tryAcquire(arg) 方法的返回值是false,!tryAcquire(arg)的返回值是true,然后我们来看acquireQueued(addWaiter(Node.EXCLUSIVE), arg),先看addWaiter(Node.EXCLUSIVE)方法,Node.EXCLUSIVE的值是null,我们进入addWaiter方法看一下,代码如下:
AbstractQueuedSynchronizer.java:
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;
}
首先mode是null,我们从第一行代码开始看起,Node类的构造方法也很简单,所以node代表装载线程ThreadB的节点,pred是null,然后执行enq(node),我们进入enq()方法,代码如下:
AbstractQueuedSynchronizer.java:
private Node enq(final Node node) {
for (;;) {
Node t = tail;
if (t == null) { // Must initialize
if (compareAndSetHead(new Node()))
tail = head;
} else {
node.prev = t;
if (compareAndSetTail(t, node)) {
t.next = node;
return t;
}
}
}
}
现在node代表装载线程ThreadB的节点,fro(;;)代表死循环, 由于tail是null,所以t是null,因此符合if判断的条件,然后开始进行初始化,现在我们来看compareAndSetHead(new Node())方法,该方法就是判断head是否为null,如果是就更新head的指向是new Node(),现在head的初始化值就是null,因此该方法执行之后head指向一个新的节点,图形描述如下:
然后执行tail=head,那么图形就变成了这样,如下:
本次for循环执行完毕了,但是for(;;)是死循环,所以需要在执行一次,由于tail此时指向傀儡节点,那么tail不是null,因此t也不是null,最终将执行else中的代码,此时node还是装载线程ThreadB的节点(简称ThreadB节点),node.prev = t代表ThreadB节点的前面方向指向傀儡节点,之后我们来执行if判断中的compareAndSetTail(t, node),该方法执行之后tail指向node,也就是tail指向ThreadB节点,之后进入if代码中执行t.next = node,由于t代表傀儡节点,之后傀儡节点的后面方向指向ThreadB节点,图形如下:
到此为止ThreadB节点的enq()方法就执行完了,然后回到addWaiter()方法,之后执行return node,返回的是ThreadB节点,由于我们分析的是acquireQueued(addWaiter(Node.EXCLUSIVE), arg),现在已经分析完addWaiter()方法,那么我们看一下acquireQueued(ThreadB节点, 1)方法是怎么执行的,点击该方法,代码如下:
AbstractQueuedSynchronizer.java:
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);
}
}
现在node是ThreadB节点,arg是1,failed是true,interrupted是false,之后进入for死循环,然后p代表傀儡节点(predecessor()方法很简单,不在分析),现在p不等于head,然后我们看if()方法,先看shouldParkAfterFailedAcquire(),点进去看代码,如下:
AbstractQueuedSynchronizer.java:
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
int ws = pred.waitStatus;
if (ws == Node.SIGNAL)
return true;
if (ws > 0) {
do {
node.prev = pred = pred.prev;
} while (pred.waitStatus > 0);
pred.next = node;
} else {
compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
}
return false;
}
里面pred是傀儡节点,node是ThreadB节点, ws是0,Node.SIGNAL是-1,因此不相等,所以执行else中的代码,compareAndSetWaitStatus(pred, ws, Node.SIGNAL)的作用就是将傀儡节点的状态从0变成-1,变化比较小,图形如下:
之后返回false,由于acquireQueued()返回中的是for死循环,for循环中的其他部分不在分析,我们继续来分析shouldParkAfterFailedAcquire()方法,由于pred是傀儡节点,所以ws是-1,Node.SIGNAL是-1,因此相等,所以执行if中的代码,直接返回true,既然shouldParkAfterFailedAcquire(p, node)的返回值是true,那么可以来看parkAndCheckInterrupt()方法,代码如下:
AbstractQueuedSynchronizer.java:
private final boolean parkAndCheckInterrupt() {
LockSupport.park(this);
return Thread.interrupted();
}
然后执行LockSupport.park(this)的时候程序就阻塞在这里了,那就没什么好看的了,我们直接来看线程ThreadC吧
3、现在让ThreadC线程来办理业务,前面的分析过程和ThreadB一致,所以不再分析,从acquire()方法开始分析吧,代码如下:
AbstractQueuedSynchronizer.java:
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
tryAcquire()方法就不在分析了,返回的是false,那么!tryAcquire()返回值就是true,那我们来分析acquireQueued(addWaiter(Node.EXCLUSIVE), arg),先看addWaiter(Node.EXCLUSIVE),代码如下:
AbstractQueuedSynchronizer.java:
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;
}
node代表装载ThreadC线程的节点(简称ThreadC节点),tail指向ThreadB节点,所以pred代表ThreadB节点,因此pred不等于null,所以执行if中的代码,node.prev = pred代表让ThreadC节点的前面指向ThreadB节点,compareAndSetTail(pred, node)代表让tail指向ThreadC节点,pred.next = node代表让ThreadB节点的后面指向ThreadC节点,之后返回ThreadC节点,图形描述如下:
然后而我们来看acquireQueued()方法,代码如下:
AbstractQueuedSynchronizer.java:
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);
}
}
直接看for死循环,p是ThreadC节点的前面,那就是ThreadB节点,由于head指向ThreadC节点, 那么不相等,那么将会执行if (shouldParkAfterFailedAcquire(p, node) &&parkAndCheckInterrupt()),我们先看shouldParkAfterFailedAcquire()方法,代码如下:
AbstractQueuedSynchronizer.java:
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
int ws = pred.waitStatus;
if (ws == Node.SIGNAL)
return true;
if (ws > 0) {
do {
node.prev = pred = pred.prev;
} while (pred.waitStatus > 0);
pred.next = node;
} else {
compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
}
return false;
}
pred是ThreadB节点,node是ThreadC节点,ws等于0,Node.SINGLE等于-1,所以不相等于,compareAndSetWaitStatus(pred, ws, Node.SIGNAL)会将ThreadB节点的waitStatus变成-1,图形如下:
最终返回false,然后在执行一次acquireQueued()中的for循环,shouldParkAfterFailedAcquire(p, node)就会返回true,之后我们来看acquireQueued()中的parkAndCheckInterrupt()方法,代码如下:
AbstractQueuedSynchronizer.java:
private final boolean parkAndCheckInterrupt() {
LockSupport.park(this);
return Thread.interrupted();
}
很明显,线程ThreadC被阻塞在这里了
4、假设现在ThreadA办完了业务,开始执行unlock()方法,我们来看代码如下:
ReentrantLock.java:
public void unlock() {
sync.release(1);
}
然后来看release方法,代码如下:
AbstractQueuedSynchronizer.java:
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()方法,代码如下:
ReentrantLock.java:
protected final boolean tryRelease(int releases) {
int c = getState() - releases;
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false;
if (c == 0) {
free = true;
setExclusiveOwnerThread(null);
}
setState(c);
return free;
}
现在releases是1,getState()的结果是1,所以c是0,当前线程正好就是ThreadA,那么和占据业务窗口的线程相等,所以不会进入if,free是false,c正好是0,那么进入if判断,free是true,我们来看setExclusiveOwnerThread(null),代码如下:
AbstractOwnableSynchronizer.java:
protected final void setExclusiveOwnerThread(Thread thread) {
exclusiveOwnerThread = thread;
}
代表将业务窗口处的线程置为null,那么图形如下:
我们回到tryRelease()方法,然后执行setState©,代码如下:
AbstractQueuedSynchronizer.java:
protected final void setState(int newState) {
state = newState;
}
c就是0,而newState等于c,那么state就是0,图形如下:
之后tryRelease()方法返回true,那么将会执行release()方法中的if代码,我们在看一下该方法代码,如下:
AbstractQueuedSynchronizer.java:
public final boolean release(int arg) {
if (tryRelease(arg)) {
Node h = head;
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);
return true;
}
return false;
}
由于head指向傀儡节点,那么h就代表傀儡节点,所以h不等于null,然后h.waitStatus等于-1,所以将会执行unparkSuccessor(h)方法,我们看该方法的代码,如下:
AbstractQueuedSynchronizer.java:
private void unparkSuccessor(Node node) {
int ws = 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);
}
node代表傀儡节点, ws是-1,然后执行compareAndSetWaitStatus(node, ws, 0)代码,现在傀儡节点的waitStatus就是-1,这行代码的作用就是将-1更新为0,图形如下:
之后执行Node s = node.next,那么s就是ThreadB节点,现在ThreadB节点不等于null,并且ThreadB节点的waitStatus是-1,所以不符合if的要求,那我们执行往下执行代码,由于ThreadB节点不等于null,那么LockSupport.unpark(s.thread)将被执行,那么代码如下:
LockSupport.java:
public static void unpark(Thread thread) {
if (thread != null)
UNSAFE.unpark(thread);
}
由于thread等于ThreadB线程,然后将线程B解锁
5、现在来解锁线程B,线程B在parkAndCheckInterrupt()方法处被阻塞,代码如下:
AbstractQueuedSynchronizer.java:
private final boolean parkAndCheckInterrupt() {
LockSupport.park(this);
return Thread.interrupted();
}
从LockSupport.park(this)向下执行,执行Thread.interrupted(),由于当前线程不被终止,所以返回false,我们找到调用parkAndCheckInterrupt()位置的代码,如下:
AbstractQueuedSynchronizer.java:
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);
}
}
那么parkAndCheckInterrupt()的返回值是false,然后继续执行for循环中的代码,现在node是ThreadB节点,那么它的前面是傀儡节点,所以p就是傀儡节点,head也指向傀儡节点,我们来看tryAcquire(arg)方法,里面的细节在分析ThreadB线程的时候已经弄完了,所以直接看最里面的方法,代码如下:
ReentrantLock.java:
final boolean nonfairTryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
if (compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
if (nextc < 0) // overflow
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
此时acquires等于1,current等于线程ThreadB,c等于0,compareAndSetState(0, acquires)会将state变成1,setExclusiveOwnerThread(current)会将业务窗口处的线程变成ThreadB,图形如下:
之后返回true,然后就可以执行AbstractQueuedSynchronizer类中acquireQueued()方法中第一个死循环的if中的代码,首先是setHead(node),现在node是ThreadB节点,我们看下setHead代码,如下:
AbstractQueuedSynchronizer.java:
private void setHead(Node node) {
head = node;
node.thread = null;
node.prev = null;
}
head指向ThreadB节点,ThreadB节点的thread变成了null,ThreadB节点的前面没有任何指向,那么图形如下:
p是傀儡节点,然后p.next = null代表将傀儡节点的后面指向变成null,图形如下:
其实现在ThreadB节点就变成了新的傀儡节点,图形如下:
之后return false,我们来到调用acquireQueued()方法的位置,代码如下:
AbstractQueuedSynchronizer.java:
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
那么if中的代码将不会执行,unlock()方法执行结束