阿昌教你看懂AQS核心流程

595 阅读8分钟

一、预备

Hello!感谢能看菜鸡阿昌的文章 ๑•̀ㅂ•́)و✧

这几天在学习阳哥的AQS,发现他简直就是艺术品。

这里记录一下AQS的核心执行流程。我们以ReentrantLock为例子,进行突破。

因为AQS涉及到Juc的知识,所以这里需要有一些前置知识如下: ≡ω≡

  • Juc知识
  • LockSupport
  • 模版设计模式
  • 重入锁/自旋锁
  • CAS
  • Unsafe

如果不晓得,请自行去补以上的知识,再看一下的内容

也是建议打开IDEA进行源码调试一起食用极佳


二、前言

1、什么是AQS

  • 字面意思

    • 英文缩写:Abstract Queued Synchronizer
    • 中文直译:抽象队列同步器
  • 技术解释

    • AQS = 资源变量State +双向虚拟队列FIFO
  • 简单解释

    通过一个资源state,来表示锁的状态。0是自由状态,大于0代表被占用。抢不到资源的线程,就放在FIFO队列中进行管理,争取下次再进行争取获取资源


2、那为什么要学AQS呢

Juc线程流程控制&锁的底层实现的基础,如:

  • ReentrantLock
  • CountDownLatch
  • ReentrantReadWriteLock
  • Semaphore
  • 等.....

3、ReentrantLock与AQS的关系

  • ReentrantLock类继承实现图

ReentrantLock中存在一个内部类Sync,他继承了AQS,所以ReentrantLock是通过构造内聚Sync类,来间接实现AQS的内容。

在这里插入图片描述

  • 任何一个Lock接口的实现类,内部都是【聚合】了一个【队列同步器】的子类完成线程访问控制的

在这里插入图片描述


4、AQS的内部体系关系

下面是我精炼出来的内部体系关系内容:省略了很多

public abstract class AbstractQueuedSynchronizer{//AQS
    private transient volatile Node head;//头节点
    private transient volatile Node tail;//尾节点
    private volatile int state;//资源State变量,默认值为0
    private static final Unsafe unsafe = Unsafe.getUnsafe();//直接操作内存的unsafe工具包
    static final class Node {
        volatile int waitStatus;//线程Node节点状态
        volatile Node prev;//前节点
        volatile Node next;//后节点
        volatile Thread thread;//Node节点的线程
    }
}

上面会看到,AQS中会将线程进行包装成一个Node节点,它有前/后节点。这里就会发现他没有一个真正的双向队列,而是通过包装线程为Node类,并指定里面前后的指向,用前/后节点指针指向来实现一个虚拟的双向队列


5、AQS同步队列的基本结构

在这里插入图片描述


三、正文

我们这里以两个线程抢资源锁为例,测试代码如下:

public static void main(String[] args) {
    ReentrantLock lock = new ReentrantLock();

    new Thread(()->{
        try {
            lock.lock();
            System.out.println("线程A拿到锁,并执行模拟业务执行60分钟");
            Thread.sleep(60*60*1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    },"线程A").start();

    new Thread(()->{
        try {
            lock.lock();
            System.out.println("线程B拿到锁");
        }finally {
            lock.unlock();
        }
    },"线程B").start();

    System.out.println("程序结束.....");
}

按照main执行流程下来,假设main线程先创建线程A,那么肯定先会执行lock.lock(),让我们看看lock()方法的底层到底做了什么,是如何通过AQS去让线程之间阻塞执行的。

1、线程A开始抢锁

①lock()

在这里插入图片描述

进来他先会执行sync的lock()方法

在这里插入图片描述

那我们上面已经知道了sync对象是Sync类,内聚变量,一开始并未对齐初始化。

在这里插入图片描述

那我们开始创建ReentrantLock对象的时候,就会对Sync进行初始化,以上是以无参构造器举例(默认是非公平锁)

在这里插入图片描述

因此,我们上面执行的sync.lock()会去执行Sync的lock()方法,而lock()方法是一个抽象方法

在这里插入图片描述

所以就会去执行我们无参构造给初始化的NonfairSynclock()方法。

在这里插入图片描述

首先进入lock()方法,他会进行if-else判断,进行执行compareAndSetState(0,1)


②compareAndSetState()

我们看方法的命名,就知道他必然是CAS方法,他肯定是调用unsafe工具包直接操作内存地址的。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vmqLbZE8-1641015862564)(../AppData/Roaming/Typora/typora-user-images/image-20220101110824640.png)]

//CAS,预期的的值是0,想要更新为1
protected final boolean compareAndSetState(int expect, int update) {//0,1
    //this:AQS队列同步器
    //stateOffset:资源变量,stateOffset内存的偏移量,也就是地址
    //expect:期盼值是0,他是默认值是0,所以肯定是为0
    //update:更新值是1
    return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
}

如果此时队列同步器AQS的state资源变量的值是0,那么就更新为1,且该操作是原子操作。再上面,我们说了state资源变量的默认值为0,那这里的CAS操作必然是成功的。就会修改完成后返回true。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hW9fW6yb-1641015862565)(../AppData/Roaming/Typora/typora-user-images/image-20220101111540775.png)]

compareAndSetState(0, 1)返回的是true,所以就会继续往下执行setExclusiveOwnerThread(Thread.currentThread())


③setExclusiveOwnerThread()

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eoCXrolS-1641015862567)(../AppData/Roaming/Typora/typora-user-images/image-20220101111716424.png)]

看这个名字都能看出来,他是设置排他Exclusive的拥有者线程是哪个

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BALmNuPR-1641015862568)(../AppData/Roaming/Typora/typora-user-images/image-20220101111736436.png)]

设置占有线程,逻辑也很简单,就是将Thread exclusiveOwnerThread变量设置为当前线程,代表此时的队列同步器处理器占有的线程的是此时进来的线程A。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KzNvvxSL-1641015862570)(../AppData/Roaming/Typora/typora-user-images/image-20220101111936210.png)]

那执行完,这里的lock()逻辑就完毕返回了,接下来就会执行业务逻辑,这里我们模拟是业务执行过长时间,线程A一直持有锁。

那么此时的全局状态图应该是如下:

在这里插入图片描述

线程A去强占资源,state资源变量通过CAS操作从0修改为1。setExclusiveOwnerThread()设置了当前AQS同步队列的占有线程为线程A


2、线程B开始抢锁

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hN7AHYra-1641015862573)(../AppData/Roaming/Typora/typora-user-images/image-20220101113049488.png)]

线程B肯定有会像上面的线程A一样去执行lock()方法

①lock()

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1lbtyZxG-1641015862575)(../AppData/Roaming/Typora/typora-user-images/image-20220101113114294.png)]

同样,他会执行到lock()方法,也会去先执行compareAndSetState()

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bhmbRWcd-1641015862580)(../AppData/Roaming/Typora/typora-user-images/image-20220101113259399.png)]

但是此时传入的参数为0,1。即期望为0,更新为1的操作,但是此时state变量已经被占用了,且因为线程A被CAS修改为1,那么结果很不意外的,compareAndSetState必然返回false,则就不会进入setExclusiveOwnerThread去改变此时AQS的拥有线程,因为此时线程A正在占用。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mC4lUXCS-1641015862584)(../AppData/Roaming/Typora/typora-user-images/image-20220101113511646.png)]

那么,他就会去执行acquire(1)的方法


②acquire()

image-20220101113511646

因为上面,直接CAS操作失败了,那么就会执行acquire

上面会看到3个重要方法:

  1. tryAcquire()
  2. addWaiter()
  3. acquireQueued()

③tryAcquire()

在这里插入图片描述

一点开,会发现,他上来就给抛了个UnsupportedOperationException异常,那就会很明显的看出来,这里使用的模版设计模式,必须要实现抽象的AbstractQueuedSynchronizertryAcquire方法。因为我们用的默认的空参构造器,所以初始化Sync用的是NonfairSync。进入之后他就会执行nonfairTryAcquire()


④nonfairTryAcquire()

在这里插入图片描述

final boolean nonfairTryAcquire(int acquires) {
    //拿到当前线程引用,这里会拿到线程B
    final Thread current = Thread.currentThread();
    //拿到此时state资源的值,此时state的值为1,被线程A给占用CAS修改为1
    int c = getState();
    // c = 1,不进入该逻辑
    if (c == 0) {
        if (compareAndSetState(0, acquires)) {
            setExclusiveOwnerThread(current);
            return true;
        }
    }
    //判断是否是可重入锁
    //判断current当前是否是占用的线程,也就是线程A,那必然不是,此时是线程B来抢资源
    //如果线程A因为某些原因放弃锁或再去获取同一把锁,这里就会进入
    else if (current == getExclusiveOwnerThread()) {
        int nextc = c + acquires;
        if (nextc < 0) // overflow
            throw new Error("Maximum lock count exceeded");
        setState(nextc);
        return true;
    }
    //因为 c = 1,上面都没有执行,所以最终返回false
    return false;
}

在这里插入图片描述

因为此时state的状态为1,那么线程B在tryAcquire()什么都不执行,并返回false,因为外面有一个!非,所以&&会继续向有执行,也就是会执行acquireQueued(addWaiter(Node.EXCLUSIVE), arg)


⑤addWaiter()

在这里插入图片描述

传入Node模式为EXCLUSIVE排他类型

private Node addWaiter(Node mode) {
    //将线程B包装成一个Node节点,模式为EXCLUSIVE排他
    Node node = new Node(Thread.currentThread(), mode);
    //pred节点为tail尾节点,但是此时尾节点的指针肯定是null
    Node pred = tail;
    //所以Node pred = null
    //进不来进行的if
    if (pred != null) {
        node.prev = pred;
        if (compareAndSetTail(pred, node)) {
            pred.next = node;
            return node;
        }
    }
    //那么就会走到这里,进行enq将线程B入队
    enq(node);
    return node;
}

⑥enq()

进行enq将线程B入队

在这里插入图片描述

上来看到是一个自旋for (;;)

private Node enq(final Node node) {
    //自旋,死循环
    for (;;) {
        //此时的tail尾节点为null
        //Node t = null
        Node t = tail;
        //进入如下逻辑
        if (t == null) {
			//初始化傀儡节点new Node(),一个空的Node,作为占位
            if (compareAndSetHead(new Node()))
                //尾节点设置为头节点指向,也就是傀儡节点
                tail = head;
        } else {
            node.prev = t;
            if (compareAndSetTail(t, node)) {
                t.next = node;
                return t;
            }
        }
    }
}
  • compareAndSetHead

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Oxno2o4m-1641015862589)(../AppData/Roaming/Typora/typora-user-images/image-20220101121718771.png)]

CAS操作,将当前队列同步器的头节点内存偏移量headOffset,预期是null,设置为上面new Node傀儡节点空Node。

  • 此时全局状态图

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uFJCaEiI-1641015862590)(../AppData/Roaming/Typora/typora-user-images/image-20220101122415152.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Zb2q2t6J-1641015862591)(../AppData/Roaming/Typora/typora-user-images/image-20220101122516482.png)]

经过这里给AQS同步队列初始化了一个空Node/傀儡节点,因为这里是自旋的for (;;),所以线程B还会再循环再执行。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NOt3mGiG-1641015862591)(../AppData/Roaming/Typora/typora-user-images/image-20220101122614536.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hEMwiI6E-1641015862592)(../AppData/Roaming/Typora/typora-user-images/image-20220101122822230.png)]

private Node enq(final Node node) {
    //自旋第二次进来
    for (;;) {
        //此时tail尾节点的指向是傀儡节点,所以t不等于null
        Node t = tail;
        if (t == null) { 
            if (compareAndSetHead(new Node()))
                tail = head;
            //进入下面的else逻辑,此时这里才为线程B的Node进行入队
        } else {
            //node为线程B的Node对象
            //设置线程B的Node对象的prev前节点为t,t就是傀儡节点
            node.prev = t;
            //CAS设置tail尾节点为线程B节点
            if (compareAndSetTail(t, node)) {
                //t傀儡节点的next后节点,指向node线程B
                t.next = node;
                return t;
            }
        }
    }
}
  • 以上队列状态图如下

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ikkXOQvG-1641015862593)(../AppData/Roaming/Typora/typora-user-images/image-20220101123502993.png)]

执行完enq(),就会返回线程B的node节点地址

在这里插入图片描述

那么接下来就会执行acquireQueued(),他传入的参数1为返回的线程B的node节点,和上面传下来的arg=1的参数。


⑦acquireQueued()

acquireQueued最为关键,就是将线程B抢不到锁后阻塞挂起

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GlYJP9Il-1641015862596)(../AppData/Roaming/Typora/typora-user-images/image-20220101123734881.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5yUJKBD5-1641015862597)(../AppData/Roaming/Typora/typora-user-images/image-20220101124021578.png)]

final boolean acquireQueued(final Node node, int arg) {
    boolean failed = true;
    try {
        boolean interrupted = false;
        //自旋,死循环
        for (;;) {
            //拿到线程B的node节点的前节点给p对象
            final Node p = node.predecessor();
            // 判断线程B的前节点是否是头节点,根据上面的全局状态图,我们可以知道现在的头节点的指向是空节点,也就是傀儡节点
            //p是傀儡节点,他是头节点,但不会进入,因为tryAcquire会再次抢锁,内容跟上面的一样,因为线程A一直占用着资源,所以不进入逻辑
            if (p == head && tryAcquire(arg)) {
                setHead(node);
                p.next = null; // help GC
                failed = false;
                return interrupted;
            }
            //判断此时线程B的Node的waitStatus状态
            //p:线程B节点的前节点指向,也就是傀儡节点
            //node:线程B节点
            if (shouldParkAfterFailedAcquire(p, node) &&
                parkAndCheckInterrupt())
                interrupted = true;
        }
    } finally {
        if (failed)
            cancelAcquire(node);
    }
}
  • shouldParkAfterFailedAcquire

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ScG59of6-1641015862598)(../AppData/Roaming/Typora/typora-user-images/image-20220101124150093.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jQXVR61F-1641015862600)(../AppData/Roaming/Typora/typora-user-images/image-20220101124425111.png)]

private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
    //拿到傀儡节点的waitStates状态,因为是类的成员变量,所以初始化默认值为0
    int ws = pred.waitStatus;
    //判断傀儡节点的waitStates是否等于 -1
    if (ws == Node.SIGNAL)
        return true;
    //傀儡节点的waitStates = 0
    if (ws > 0) {
        do {
            node.prev = pred = pred.prev;
        } while (pred.waitStatus > 0);
        pred.next = node;
    } else {
        //执行这里,通过CAS操作,将傀儡节点的waitStates设置为-1
        compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
    }
    //返回false
    return false;
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-S6BfJu0A-1641015862600)(../AppData/Roaming/Typora/typora-user-images/image-20220101124705851.png)]

因为这里是自旋的死循环,所以还会再次进入执行。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RKEHuYGK-1641015862601)(../AppData/Roaming/Typora/typora-user-images/image-20220101124719956.png)]

final boolean acquireQueued(final Node node, int arg) {
    boolean failed = true;
    try {
        boolean interrupted = false;
        //第二次进入
        for (;;) {
            //拿到线程B的前节点指向,也就是傀儡节点
            //p为傀儡节点指向
            final Node p = node.predecessor();
            //同样不进入如下逻辑,模拟线程A还在占用资源并未结束
            if (p == head && tryAcquire(arg)) {
                setHead(node);
                p.next = null; // help GC
                failed = false;
                return interrupted;
            }
            //再次进入shouldParkAfterFailedAcquire
            if (shouldParkAfterFailedAcquire(p, node) &&
                parkAndCheckInterrupt())
                interrupted = true;
        }
    } finally {
        if (failed)
            cancelAcquire(node);
    }
}
  • 全局状态图

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-snSJ1hEF-1641015862603)(../AppData/Roaming/Typora/typora-user-images/image-20220101125100609.png)]

因为我们在shouldParkAfterFailedAcquire给p节点,也就是傀儡节点设置了他的waitState为Node.SIGNAL,也就是-1

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MwRT0FxR-1641015862605)(../AppData/Roaming/Typora/typora-user-images/image-20220101125157406.png)]

第二次进入再次判断傀儡节点的waitStatus,因为上面设置为了-1,所以这次就会返回了true。那么接下来就会执行parkAndCheckInterrupt

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PDWdgbcj-1641015862608)(../AppData/Roaming/Typora/typora-user-images/image-20220101125242797.png)]

  • parkAndCheckInterrupt()

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kZ2JCil2-1641015862610)(../AppData/Roaming/Typora/typora-user-images/image-20220101125500624.png)]

出现了!!!这里就是让线程B阻塞的罪魁祸首LockSupportpark,这个this就是线程B。那么这里线程B就需要有人执行LockSupport.unpark(线程B)来唤醒他,不然他就会永远被阻塞挂起

  • 全局状态图

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uL26Aaq5-1641015862612)(../AppData/Roaming/Typora/typora-user-images/image-20220101125621559.png)]

接下来就是等待持有资源的线程A执行unlock()方法了


3、线程A放弃锁

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LYe5TRKU-1641015862616)(../AppData/Roaming/Typora/typora-user-images/image-20220101125638116.png)]

线程A执行玩逻辑之后,最终finally就会执行lock.unlock(),进行解锁,放弃锁资源

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DOe6Mhds-1641015862617)(../AppData/Roaming/Typora/typora-user-images/image-20220101125718552.png)]

这里也能看出,ReentrantLock的unlock方法,依然是调用sync的release方法

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-haWJsKSG-1641015862619)(../AppData/Roaming/Typora/typora-user-images/image-20220101125815616.png)]

会看到这里他会先进入if,去执行tryRelease,同样,这个tryRelease执行的必然是实现类的方法,也就是Sync的tryRelease,模版设计模式


①tryRelease

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-huEF9BgH-1641015862620)(../AppData/Roaming/Typora/typora-user-images/image-20220101125945274.png)]

protected final boolean tryRelease(int releases) {
    //getState()获取当前资源state值,看上面的全局状态,此时为1
    //releases为传入的值1
    //c = 1 -1 
    //c = 0
    int c = getState() - releases;
    //判断当前线程是否是资源占用线程
    if (Thread.currentThread() != getExclusiveOwnerThread())
        throw new IllegalMonitorStateException();
    boolean free = false;
    //进入如下逻辑
    if (c == 0) {
        free = true;
        //将当前占用资源的线程改为null
        setExclusiveOwnerThread(null);
    }
    //并设置当前state资源变量为c,也就是0
    setState(c);
    return free;
}
  • 全局状态图

在这里插入图片描述

public final boolean release(int arg) {
    //上面返回的free为true,进入下面逻辑
    if (tryRelease(arg)) {
        //拿到头节点的引用并赋给h变量,头节点是傀儡节点
        //h为傀儡节点
        Node h = head;
        //头节点为傀儡节点不为null,且傀儡节点的waitState状态为-1,不等于0
        if (h != null && h.waitStatus != 0)
            //执行unparkSuccessor,唤醒阻塞的线程
            unparkSuccessor(h);
        return true;
    }
    return false;
}

②unparkSuccessor

这个方法会唤醒,上面阻塞的线程B

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-T4EcxA5B-1641015862624)(../AppData/Roaming/Typora/typora-user-images/image-20220101130501527.png)]

private void unparkSuccessor(Node node) {
    //拿到傀儡节点的waitStatus状态
    //ws = -1
    int ws = node.waitStatus;
    //进入逻辑
    if (ws < 0)
        //CAS,设置傀儡节点的waitStatus为0
        compareAndSetWaitStatus(node, ws, 0);
    //拿到傀儡节点的后节点,也就是线程B节点
    //s为线程B节点
    Node s = node.next;
    //此时s为线程B节点不为null,且线程B节点的waitStatus为0,不进入逻辑
    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;
    }
    //s为线程B节点不为null,进入逻辑
    if (s != null)
        //为线程B唤醒
        LockSupport.unpark(s.thread);
}

LockSupport.unpark(s.thread)为线程B唤醒,所以线程B就是开始执行。这里线程A的unlock()就执行完毕了

  • 全局状态图

在这里插入图片描述


4、线程B被线程A唤醒,并尝试去再抢锁

线程B被线程A唤醒,并自旋死循环,再次强占锁资源

①acquireQueued

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mWAsNpcF-1641015862627)(../AppData/Roaming/Typora/typora-user-images/image-20220101132117310.png)]

final boolean acquireQueued(final Node node, long arg) {
    boolean failed = true;
    try {
        boolean interrupted = false;
        //被线程A唤醒的线程B再次进入
        for (;;) {
            //拿到线程B的前节点,也就是傀儡节点
            //p为傀儡节点
            final Node p = node.predecessor();
            //此时傀儡节点为头节点,且再次执行tryAcquire去强占资源,此时会返回true,并强占成功
            if (p == head && tryAcquire(arg)) {
                //设置头节点为线程B
                //设置线程B的Node中的线程为null
                //线程B的Node的前节点为null
                setHead(node);
                //将傀儡节点的后节点设置为null
                p.next = null; 
                failed = false;
                //返回是否被中断,默认是没被中断也是false
                return interrupted;
            }
            if (shouldParkAfterFailedAcquire(p, node) &&
                parkAndCheckInterrupt())
                interrupted = true;
        }
    } finally {
        if (failed)
            cancelAcquire(node);
    }
}
  • tryAcquire()

同样是模版设计模式,会去执行Sync的实现

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9lIRQr9t-1641015862629)(../AppData/Roaming/Typora/typora-user-images/image-20220101132530000.png)]

  • nonfairTryAcquire()
final boolean nonfairTryAcquire(int acquires) {
    //拿到当前线程B
    final Thread current = Thread.currentThread();
    //获取当前的资源State变量,此时为0,因为已经被线程A的unlock方法设置为了0
    int c = getState();
    //进入逻辑
    if (c == 0) {
        //将资源State变量设置为传入的acquires,也就是1
        if (compareAndSetState(0, acquires)) {
            //再设置当前资源占用的线程为当前线程,也就是线程B
            setExclusiveOwnerThread(current);
            //返回true
            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;
}
  • setHead()

设置线程B的Node为头节点,设置线程B的Node中的线程为null,线程B的Node的前节点为null

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-iHeavONT-1641015862631)(../AppData/Roaming/Typora/typora-user-images/image-20220101133223868.png)]

  • 全局状态图

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BIEWlNsI-1641015862643)(../AppData/Roaming/Typora/typora-user-images/image-20220101133531677.png)]

以此循环,如果有别的线程加入队列也是维持以上的流转管理操作。

四、结尾

以上就满足了AQS的管理流转,通过资源变量State+AQS的同步管理队列,实现了线程强占资源的管理。

资源变量State来控制当前资源是否被占用,0为未占用状态,大于0为被占用情况。

抢不到资源的线程会被AQS维护管理在虚拟的同步管理队列,通过LockSupprot进行线程的唤醒和阻塞。

现在再细品一开始的话:

通过一个资源state,来表示锁的状态。0是自由状态,大于0代表被占用。抢不到资源的线程,就放在FIFO队列中进行管理,争取下次再进行争取获取资源

以上就是这次的全部内容,感谢你能看到这里(๑ˉ∀ˉ๑)!!!