通过ReentrantLock的lock()、unlock()方法来分析AQS

目录

代码

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()方法执行结束

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值