AQS源码全流程分析

AQS源码分析和图解

    最近在研究AQS源码的时候,找了一些网上的博客和论坛,发现描述的都大同小异,每个人的说法都不太完全统一,后来找了一些网上推荐的一些书籍,书籍介绍的知识点倒还挺全面的,但是,自己看书的话,书本知识点不容易串联起来,理解起来有点吃力,于是便自己亲自调试源码,并写下本博客,希望大家可以参考参考,有什么不对的地方欢迎大家留言指正。
    本文调试的代码如下:

public class ConditionTest {
    static final Lock lock = new ReentrantLock();
    static final Condition condition = lock.newCondition();

    public static void main(String[] args) throws Exception{
        final Thread thread1 = new Thread("Thread 1 "){
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName() + " 准备获取锁 .....");
                lock.lock(); // 线程 1获取 lock
                try {
                    System.out.println(Thread.currentThread().getName() + " 准备调用await(等待一个signal)  ..... ");
                    condition.await(); // 调用 condition.await 进行释放锁, 将当前节点封装成一个 Node 放入 Condition Queue 里面, 等待唤醒
                    System.out.println(Thread.currentThread().getName() + " 获得一个 signal,await调用结束  ..... ");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() + "  准备调用unlock ");
                lock.unlock(); // 释放锁
            }
        };
        thread1.start();  // 线程 1 线运行
        Thread.sleep(4 * 1000);
        Thread thread2 = new Thread("Thread 2 "){
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName() + " 准备获取锁 .....");
                lock.lock();        // 线程 2获取lock
                System.out.println(Thread.currentThread().getName() + " 准备中断 [Thread 1]  .....");
                thread1.interrupt(); // 对线程1 进行中断 看看中断后会怎么样? 
                try {
                    Thread.sleep(10 * 1000);
                }catch (Exception e){
                }
                System.out.println(Thread.currentThread().getName() + " 准备发送一个 signal 信号");
                condition.signal(); // 发送唤醒信号 从 AQS 的 Condition Queue 里面转移 Node 到 Sync Queue
                System.out.println(Thread.currentThread().getName() + " 发送 signal 结束,准备unlock");
                lock.unlock(); // 线程 2 释放锁
            }
        };
        thread2.start();
        Thread.sleep(4 * 1000);
        Thread thread3 = new Thread("Thread 3 "){
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName() + " 准备获取锁 .....");
                lock.lock(); // 线程 3 获取 lock
                try {
                    System.out.println(Thread.currentThread().getName() + " 准备调用await(等待一个signal)  ..... ");
                    condition.await(); // 调用 condition.await 进行释放锁, 将当前节点封装成一个 Node 放入 Condition Queue 里面, 等待唤醒
                    System.out.println(Thread.currentThread().getName() + " 获得一个 signal,await调用结束  ..... ");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() + "  准备调用unlock ");
                lock.unlock(); // 释放锁
            }
        };
        thread3.start();
    }
}

本文分成三大部分,分别是源码调试和图解、源码分析、归纳总结

1. 源码调试和图解
以下截图是本地调试的结果,分为代码断点截图(如图1-1所示)和AQS状态图(如图2所示),AQS状态图是程序运行到断点时,根据各参数情况绘制的,每个AQS状态图和上一个AQS状态图的变化均用红色笔迹标识,如图2和图4红色字迹的变化。本文约定,LockSupport.park()方法表示线程阻塞挂起,LockSupport.unpark()表示线程唤醒


图1-1 Thread 1调用lock方法

图1-2 Thread 1调用lock方法

图1-3 Thread 1调用lock方法

图2 AQS的状态信息变化


 Thread 1获取独占锁,并且把状态state赋值为1,此时AQS状态信息表示Thread 1获取了独占锁。


图3-1 Thread 2调用lock方法过程

图3-2 Thread 2调用lock方法过程

图3-3 Thread 2调用lock方法过程

图3-4 Thread 2调用lock方法过程

图3-5 Thread 2调用lock方法过程


 Thread 2获取独占锁失败。(刚才被Thead 1赋值了信息,也就是被Thread 1获取了锁)


图3-6 Thread 2调用lock方法过程

图3-7 Thread 2调用addWaiter方法

图3-8 Thread 2进入enq方法的if判断

图 4 AQS锁队列增加首结点


 Thread 2创建锁队列节点(首节点,代表Thread 1),并把自己的Thread 2节点加入锁队列。


图5 进入enq方法的else判断

图 6 AQS增加Thread 2节点到锁队列

图7-1 Thread 2退出addWaiter方法

图7-2 Thread 2进入acquireQueued方法

图7-3 Thread 2第一次调用shouldParkAfterFailedAcquire方法

图7-3 修改Thread 2上一个节点waitStatus状态

图7-4 shouldParkAfterFailedAcquire方法返回false

图8 锁队列节点的waitStatus变化

图9-1 Thread 2进入acquireQueued的下一个for循环

图9-2 Thread 2第二次调用shouldParkAfterFailedAcquire方法

图9-3 Thread 2返回true

图9-4 Thread 2进入parkAndCheckInterrupt方法

图9-5 Thread 2阻塞挂起

图10 锁队列节点线程信息的变化

图11-1 Thread 3调用lock方法过程

图11-2 Thread 3调用lock方法过程

图11-3 Thread 3进入addWaiter方法

图11-4 运行addWaiter的if判断

图12 锁队列增加Thread 3节点

图13-1 Thread 3进入acquireQueued方法

图13-2 Thred 3第一次调用shouldParkAfterFailedAcquire方法

图13-3 Thread 3设置上一个节点的waitStatus状态

图13-4 Thread 3运行的shouldParkAfterFailedAcquire方法返回false

图14 Thread 3节点的上一个节点状态发生变化

图15-1 Thread 3第二次进入shouldParkAfterFailedAcquire方法

图15-2 Thread 3进入阻塞状态

图16 锁队列中Thread 3节点阻塞

图17-1 Thread 1进入await方法

图17-2 Thread 1调用addConditionWaiter方法

图17-3 执行addConditionWaiter方法

图18 增加等待队列节点

图19-1 Thread 3调用fullRease方法

图19-1 Thread 3调用fullRease方法过程

图19-1 Thread 3调用fullRease方法过程

图19-1 Thread 3把AQS状态信息恢复

图20 AQS状态信息恢复

图21-1 Thread 3调用unparkSuccessor方法

图21-2 Thread 3把锁队列首节点waitStatus置为0并唤醒下一个节点

图22 锁队列节点信息变化

图23-1 fullReleae方法返回

图23-2 fullReleae方法返回

图23-3 不执行fullReleae的finally方法

图23-4 Thread 3开始调用isOnSyncQueued方法

图23-5 Thread 3开始调用isOnSyncQueued方法过程

图23-6 Thread 3进入阻塞状态

图24 等待队列节点进入阻塞

图25-1 Thread 2被唤醒

图25-2 Thread 2执行acquireAueued的if判断

图26 锁队列的首节点变化

图27-1 Thread 2执行acquireQueued的finally判断

图27-2 Thread 2结束acquire的方法

图27-3 Thread 2结束lock方法调用

图27-4 Thread 2中断Thread 1

图27-5 Thread 1被Thread 2的中断唤醒

图28 等待队列首节点Thread 1被唤醒

图29-1 Thread 1进入checkInterruptWhileWaiting方法

图29-2 进入transferAfterCancelledWait方法

图30 等待队列的首节点发生转移

图31-1 结束checkInterruptWhileWaitting的调用,返回0

图31-2 执行acquireQueued方法

图31-3 Thread 1进入parkAndCheckInterrupt方法

图31-4 Thread 1在锁队列中挂起

图31-5 Thread 2继续执行signal方法

图31-6 Thread 2调用dosignal方法

图31-7 Thread 2调用dosignal方法过程

图32 等待队列节点的去除及waitstatus状态的变化

图33-1 把Thread 1节点的waitstatus置为0

图34 把转移的节点waitStatus位置0

图35-1 结束doSignal调用

图35-2 Thread 2调用unlock方法

图35-3 Thread 2调用release方法过程

图35-4 Thread 2重置独占锁信息并调用unparkSuccessor

图35-5 唤醒Thread 2的后继节点

图36 重置独占锁并唤醒首节点的后继节点

图37-1 Thread 2的release方法返回true

图37-2 Thread 2调用unlock结束

图37-3 Thread 2运行结束

图37-4 Thread 3被唤醒

图37-5 Thread 3开始执行下一个for循环

图37-6 Thread 3获得首节点位置,并去除原来的Thread 2节点

图38 重置首节点为Thread 3(Thread 2被GC)

图39-1 Thread 3执行返回acquire方法

图39-2 Thread 3执行返回lock方法

图39-3 Thread 3获取锁并执行await方法

图39-4 Thread 3在等待队列新加节点,并阻塞在等待队列

图40 Thread 3进入等待队列之后的队列信息

图41-1 Thread 1由于被唤醒,继续执行

图41-2 Thread 1变成首节点

图42 Thread 1变成锁队列首节点

图43-1 Thread 1继续执行await的最后两个if判断

图43-2 Thread 1结束await方法调用

图43-3 Thread 1调用unlock方法

图44 锁队列信息变化

图45-1 Thread 1调用unlock结束

图图45-2 只有Thread 3处于等待状态

图46 最终AQS的队列状态信息

2 源码分析
 根据第一部分的AQS状态变化,进行源码分析:


(1)首先,分析Node节点,锁队列节点和等待队列节点都是使用这个数据结构


Node源码分析

static final class Node {
        
        static final Node SHARED = new Node();
       
        static final Node EXCLUSIVE = null;

        static final int CANCELLED =  1;// 标志等待超时或被中断
        static final int SIGNAL    = -1;// 表示释放锁之后,是否通知后一个节点
        static final int CONDITION = -2;// 表示节点在等待队列中
        static final int PROPAGATE = -3;// 共享模式中使用,运行状态
        volatile int waitStatus;  // 表示等待状态,有CANCELLED 、SIGNAL、PROPAGATE 、PROPAGATE 和 0
        volatile Node prev; // 表示上一个结点的引用
        volatile Node next; // 表示后继结点的引用
        volatile Thread thread; // 当前结点的线程名称
        Node nextWaiter; // 代表下一个等待队列结点的引用,在等待队列中有用

        final boolean isShared() {
            return nextWaiter == SHARED;
        }

       // 获取上一个结点
        final Node predecessor() throws NullPointerException {
            Node p = prev;
            if (p == null)
                throw new NullPointerException();
            else
                return p;
        }


       //创建首结点和共享结点
        Node() {}

       // 用于创建锁队列结点
        Node(Thread thread, Node mode) {      
            this.nextWaiter = mode;
            this.thread = thread;
        }

       //创建等待队列节点
        Node(Thread thread, int waitStatus) { 
            this.waitStatus = waitStatus;
            this.thread = thread;
        }
    }


(1)addWaiter方法和enq方法用于把新结点加入锁队列

addWaiter源码分析

//将当前线程加入到锁队列的尾部,并返回当前线程所在的结点
    private Node addWaiter(Node mode) {
        Node node = new Node(Thread.currentThread(), mode);//EXCLUSIVE(独占)和SHARED(共享)
        // 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;
    }


enq源码分析

   // 锁队列没有结点时候的入队方法
    private Node enq(final Node node) {
        for (;;) { // CAS + 循环 = 自旋
            Node t = tail;
            if (t == null) { //  队列为空
                if (compareAndSetHead(new Node()))// 创建一个空的标志结点作为head结点
                    tail = head; // tail 和 head都是同一个节点
            } else {
                node.prev = t; // 新节点的前继结点指向尾结点
                if (compareAndSetTail(t, node)) { // 把尾节点指向新结点
                    t.next = node;  // 添加前的尾结点指向新加的结点
                    return t;
                }
            }
        }
    }


(2)acquireQueued方法用于标记新增节点的前继结点为SIGNAL状态(shouldParkAfterFailedAcquire方法)、阻塞(parkAndCheckInterrupt方法)、循环判断前继结点是否头结点并获取到锁


acquireQueued源码分析

// 进入park状态,直到其他线程释放资源后唤醒继续执行
    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)) { // 如果前一个节点是head,尝试抢一次锁
                    setHead(node); // 更换head
                    p.next = null; // help GC
                    failed = false;
                    return interrupted;
                }
                if (shouldParkAfterFailedAcquire(p, node) &&// 检查状态,把前继结点标志为SIGNAL
                    parkAndCheckInterrupt())// 用于阻塞
                    interrupted = true; // 如果出现中断,则修改标记
            }
        } finally {
            if (failed)
                cancelAcquire(node);
        }
    }


parkAndCheckInterrupt源码分析

private final boolean parkAndCheckInterrupt() {
        LockSupport.park(this); // 阻塞挂起
        return Thread.interrupted(); // 唤醒,返回是否有中断迹象
    }


shouldParkAfterFailedAcquire源码分析

   //检查状态,是否需要挂起线程
    private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
        int ws = pred.waitStatus; // 根据 前置节点的状态 执行不同的流程
        if (ws == Node.SIGNAL) // 前置节点释放锁之后会通知当前线程,则后续会进行阻塞
            return true;
        if (ws > 0) { // 前置节点处于CANCELLED状态,跳过它继续寻找正常的节点,并且去掉中间那段不正常的节点
            do { // 也可以理解为,这是一次队列检查
                node.prev = pred = pred.prev;
            } while (pred.waitStatus > 0);
            pred.next = node;
        } else {
        //把前继结点标记为SIGNAL,前继节点释放锁后唤醒本节点
            compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
        }
        return false;
    }


(3)await源码解析

// 等待队列新增结点,释放锁状态,首结点的下一个结点唤醒并重置为首结点
public final void await() throws InterruptedException {
            if (Thread.interrupted())
                throw new InterruptedException();
            Node node = addConditionWaiter();// 等待队列中新增结点
            int savedState = fullyRelease(node);// 释放当前获取的锁,从头到尾把锁队列的waitStatus位置0,并唤醒后继结点
            int interruptMode = 0;
            while (!isOnSyncQueue(node)) { // 释放在锁队列
                LockSupport.park(this);  // 等待队列的新增节点阻塞挂起
                if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)//把等待队列的结点waitstatus置为0,并加入到锁队列,此时并存在两个队列
                    break;
            }
            if (acquireQueued(node, savedState) && interruptMode != THROW_IE) // 不断自旋,等待被唤醒(在锁队列中阻塞挂起)
                interruptMode = REINTERRUPT;
            if (node.nextWaiter != null) // 清除cancell的结点
                unlinkCancelledWaiters();
            if (interruptMode != 0)
                reportInterruptAfterWait(interruptMode);
        }


(4)doSignal源码解析

// 循环一遍等待队列,不断把等待队列的waitstatus置为0
 private void doSignal(Node first) {
            do {
                if ( (firstWaiter = first.nextWaiter) == null)
                    lastWaiter = null;
                first.nextWaiter = null;
            } while (!transferForSignal(first) &&
                     (first = firstWaiter) != null);
        }


transferForSignal源码解析

final boolean transferForSignal(Node node) {
        // 把等待队列的waitstatus置为0
        if (!compareAndSetWaitStatus(node, Node.CONDITION, 0))
            return false;
        Node p = enq(node);  // 加入到锁队列,返回前继结点
        int ws = p.waitStatus; // 前继节点是CANCELL或者重置SIGNAL失败,则唤醒当前结点
        if (ws > 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL))
            LockSupport.unpark(node.thread);
        return true;
    }


(5)release源码解析

public final boolean release(int arg) {
        if (tryRelease(arg)) { // 尝试把exclusiveOwnThread和state重置初始状态
            Node h = head;
            if (h != null && h.waitStatus != 0)
                unparkSuccessor(h);  // 唤醒后继节点
            return true;
        }
        return false;
    }


unparkSuccessor源码解析

private void unparkSuccessor(Node node) {
       
        int ws = node.waitStatus;
        if (ws < 0)
            compareAndSetWaitStatus(node, ws, 0);// 当前结点的waitstatus置为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); // 唤醒后续有用的结点
    }
  1. 归纳总结

    (1)获取锁的时候,ExclusiveOwnThread和state对应线程信息,tryAcquire和tryRelease对应修改这两个字段的信息,分别尝试获取锁和释放锁

    (2)addWaiter方法和enq方法用于锁队列结点的添加,并更新head和tail指针

    (3)acquireQueued方法作用:把前继节点的waitStatus赋值为SIGNAL,阻塞挂起(唤醒加锁并把自己设置为锁队列首节点)。

    (3)await方法作用:等待队列加入新结点、释放锁并唤醒head的后继结点尝试获取首节点、等待队列首结点阻塞挂起(唤醒:等待队列首结点被唤醒并把结点转移到锁队列尾部并唤醒、把尾结点的前继节点waitStatus赋值为SIGNAL、锁队列尾结点挂起阻塞)

    (4)signal方法作用:把等待队列首节点转移到锁队列尾结点,并把waitstatus赋值为0

    (5)release方法作用:释放exclusiveThread和state的信息
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值