14.AQS详细Debug解析

AQS详解

AQS 全程AbstractQueuedLongSynchronizer 抽象队列同步器

用来构建锁或者其他同步器组件的重量级基础框架及整个JUC体系的基石,通过内置的FIFO队列来完成资源获取线程的排队工作,并通过一个int state变量来表示持有锁的状态

  • 抢到资源的线程直接继续执行,抢占不到资源的线程必然涉及到一个队列等候机制
  • 如果共享资源被占用,就需要阻塞等待唤醒的机制保证锁分配
  • 队列机制在AQS中主要是用CLH队列的变体实现,将暂时获取不到锁的线程加入队列中
  • CLH队列将请求共享资源的线程封装成队列的节点 Node,通过CAS,自旋以及LockSupport的park方法,维护state变量的状态,使得并发达到同步的效果

AQS使用一个volatile的int类型成员变量来表示同步状态,通过内置的FIFO队列来完成资源获取队列,队列中每条抢占资源线程封装成一个Node节点来实现锁分配,通过CAS完成对state的修改

AQS底层本质上是通过volatile修改的state变量+FIFO的队列+CAS来完成.

/**
* The synchronization state. AQS上的state状态 0表示没有线程占用 >1表示已经被其他线程占用
*/
private volatile long state;

static final class Node {
    // 共享模式
    static final Node SHARED = new Node();
    // 排他锁模式
    static final Node EXCLUSIVE = null;
    // waitStatus中的可用值之一 表示线程被取消
    static final int CANCELLED =  1;
    // 同上 当前节点线程锁释放之后,后继线程需要被unpark
    static final int SIGNAL    = -1;
    // 节点在等待队列中wait,当其他线程对condition调用了singal,节点从等待队列加入到同步队列中
    static final int CONDITION = -2;
    // 共享模式下 直接传播下去
    static final int PROPAGATE = -3;
    volatile Node prev;
    volatile Node next;
    volatile Thread thread;
    Node nextWaiter;
    private transient volatile Node head;
    private transient volatile Node tail;
    private volatile long state;
    // 表示相当节点的等待状态的变量
    volatile int waitStatus;
}

AQS底层使用LookSupport.park和unpark来进行排队,Lock接口的实现类基本都通过聚合了一个AQS的子类来完成堆线程的访问控制

如ReentrantLock中有静态内部类Sync实现了AQS

abstract static class Sync extends AbstractQueuedSynchronizer

他又有FairSync 和 NonfairSync 子类来完成对公平锁和非公平锁的实现

源码debug AQS底层

  1. ReentrantLock#lock方法底层调用了内部类Sync的lock方法Sync的lock方法调用了公平FairSync或者非公平锁NonFairSync的lock方法

  2. 第一个线程进来先进行CAS判断修改state的值 将0改为1

    final void lock() {
        if (compareAndSetState(0, 1))
            setExclusiveOwnerThread(Thread.currentThread());
        else
            acquire(1);
    }
    
  3. 第一个线程CAS成功进入setExclusiveOwnerThread(Thread.currentThread())传入当前线程设置排他线程 本质方法设置了一个AQS父类AbstractOwnableSynchronizer的exclusiveOwnerThread属性,此时lock方法结束

        protected final void setExclusiveOwnerThread(Thread thread) {
            exclusiveOwnerThread = thread;
        }
    
  4. 第二个线程进来之后因为CAS 修改state的时候失败 进入acquire(1)方法

    public final void acquire(int arg) {
        if (!tryAcquire(arg) &&
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
            selfInterrupt();
    }
    
  5. acquire方法首先进行两个判断分别是tryAcquire(arg)和acquireQueued(addWaiter(Node.EXCLUSIVE), arg) 此时arg 参数是1

  6. 先进入tryAcquire方法,此时父类方法直接抛出了一个异常,必须子类去实现

    protected boolean tryAcquire(int arg) {
        throw new UnsupportedOperationException();
    }
    
  7. 以NonFairSync为例实现了tryAcquire方法,底层又调用了nonfairTryAcquire方法acquires值还是1

    protected final boolean tryAcquire(int acquires) {
        return nonfairTryAcquire(acquires);
    }
    
  8. 进入nonfairTryAcquire方法,进一步分析 尝试获取锁

    final boolean nonfairTryAcquire(int acquires) {
        // 获取当前线程
        final Thread current = Thread.currentThread();
        // 获取最新的AQS的state属性,因为可能出现刚好执行这块代码的时候,第一个线程已经释放了锁
        int c = getState();
        if (c == 0) {
            // 如果前一个线程已经释放了锁调用CAS修改state状态 改为1,本质跟lock方法第一次进入执行的一样 然后返回true
            if (compareAndSetState(0, acquires)) {
                setExclusiveOwnerThread(current);
                return true;
            }
        }
        // 继续判断如果当前线程跟AQS父类排他线程的exclusiveOwnerThread属性一样说明还是该线程进行加锁,递归锁的体现 此时state+1 然后重新设置回state属性 返回true
        else if (current == getExclusiveOwnerThread()) {
            int nextc = c + acquires;
            if (nextc < 0) // overflow
                throw new Error("Maximum lock count exceeded");
            setState(nextc);
            return true;
        }
        // 尝试获取锁失败 返回false
        return false;
    }
    
  9. 根据上面尝试获取tryAquire返回,如果上一个线程还没释放锁最终tryAquire会返回false取反为true继续进入acquireQueued(addWaiter(Node.EXCLUSIVE), arg))逻辑, 在进入addWaiter 方法查看addWaiter方法实现了核心的队列逻辑

       public final void acquire(int arg) {
           // tryAcquire获取锁失败返回false取反进入下一个逻辑
            if (!tryAcquire(arg) &&
                // acquireQueued方法之前先执行addWaiter方法,传入Node的排他属性
                acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
                selfInterrupt();
        }
    
  10. addWaiter方法追踪,第一个进入排队队列的线程,此时队列还没初始化head和tail都是null会进入enq 方法进行队列的初始化

    private Node addWaiter(Node mode) {
        // 首先将当前线程封装成一个Node节点,传入默认为排他模式
        Node node = new Node(Thread.currentThread(), mode);
        // 此时将Node的tail尾结点提出来
        Node pred = tail;
        // 第一个排队的线程此时tail和head的都是null不进去if逻辑
        if (pred != null) {
            node.prev = pred;
            if (compareAndSetTail(pred, node)) {
                pred.next = node;
                return node;
            }
        }
        // 第一个排队线程进去进行队列的初始化enq(node)方法
        enq(node);
        return node;
    }
    
  11. 追踪enq(node)方法,为了初始化头节点,上来直接一个死循环,判断当前AQS的等待队列是不是为空,如果是空new一个哑结点Node作为头节点,再进入循环,将封装了线程的Node挂在哑结点后面

    private Node enq(final Node node) {
        for (;;) {
            // 判断队列的尾结点是不是null
            Node t = tail;
            if (t == null) { // Must initialize
                // 第一个排队线程tail为null,将队列的head和tail都设置成一个新new的node节点
                // 注意!!这是新new的一个哑结点并不是真实的封装了线程的node
                if (compareAndSetHead(new Node()))
                    // 将tail和 head 都指向这个新new的哑结点
                    // 注意此时for循环并没有返回继续再进入for逻辑
                    tail = head;
            } else {
                // 根据上次判断逻辑t已经不是null了 而是新new出来的哑结点
                // 将当前封装了Thread的node节点的前指针指向哑结点
                node.prev = t;
                // 进行CAS操作 将t也就是哑结点改成node 设置到尾部
                if (compareAndSetTail(t, node)) {
                    // t.next = node 将哑结点的next指针指向node
                    t.next = node;
                    // 返回哑结点
                    //  此时FIFO队列变成了
                    // head		  tail
                    //   |		   |
                    // 哑结点	->  node -> null
                    // 此时addWaiter方法返回
                    return t;
                }
            }
        }
    }
    
  12. 继续返回acquire方法 准备进acquireQueued(addWaiter(Node.EXCLUSIVE), arg)

    public final void acquire(int arg) {
        if (!tryAcquire(arg) &&
            // addWaiter返回的是封装了Thread的Node节点 arg=1
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
            selfInterrupt();
    }
    
  13. 分析acquireQueued方法,此处调用了LockSupport的park方法将线程挂起,也体现了自旋锁的思想

    // node是封装了排队Thread的node arg=1
    final boolean acquireQueued(final Node node, int arg) {
        boolean failed = true;
        try {
            boolean interrupted = false;
            // 死循环
            for (;;) {
                // 获取node的上一个节点 此处是哑结点 
                final Node p = node.predecessor();
                // p==head 此时哑结点就是head true
                // 仅需tryAcquire尝试获取锁
                if (p == head && tryAcquire(arg)) {
                    // 如果获取成功 将头节点改成当前节点
                    setHead(node);
                    // 前驱节点的next指针断掉 让GC可以回收
                    p.next = null; // help GC
                    failed = false;
                    // 获取到锁成功 返回interrupted为false退出循环
                    return interrupted;
                }
                // 当前节点的前驱节点不是head头节点并且尝试获取锁失败进入下面逻辑
                // 此处的逻辑体现了LockSupport的加锁逻辑
                if (shouldParkAfterFailedAcquire(p, node) &&
                    parkAndCheckInterrupt())
                    interrupted = true;
            }
        } finally {
            if (failed)
                cancelAcquire(node);
        }
    }
    
  14. 首先进去shouldParkAfterFailedAcquire(p, node)进行判断 p为前驱节点, node为当前node,这个步骤结束之后哑结点的waitStatus从0改成了-1,返回了false

    private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
        // 第一次进来哑结点的waitStatus没有赋值为默认值0
        int ws = pred.waitStatus;
        //   static final int SIGNAL    = -1;
        // 第一次ws不等于-1不进入这个分支
        if (ws == Node.SIGNAL)
            // 如果等于-1 说明该node已经准备被唤醒了
            return true;
        // ws=0不进入
        if (ws > 0) {
            do {
                node.prev = pred = pred.prev;
            } while (pred.waitStatus > 0);
            pred.next = node;
        } else {
            // 将前驱节点的waitStatus的状态CAS从0改成-1
            compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
        }
        return false;
    }
    
  15. shouldParkAfterFailedAcquire返回了false进入packAndCheckInterrupt方法

    if (shouldParkAfterFailedAcquire(p, node) &&
        parkAndCheckInterrupt())
        interrupted = true;
    
  16. 进入parkAndCheckInterrupt方法本质就调用LockSupport的park方法将线程阻塞

    private final boolean parkAndCheckInterrupt() {
        LockSupport.park(this);
        return Thread.interrupted();
    }
    
  17. 经过上述的步骤第一个线程B已经进入了AQS的等待队列

    AQS队列的状态为

    head tail

    哑结点 -> Node(Thread_B) -> null

    哑结点的waitStatus=-1 Node(Thread_A) 的waitStatus=0


第三个线程C进入时,此时ThreadA还是执行 ThreadB已经进入等待队列此时的源码debug

  1. 还是从lock方法出发 还是以非公平锁为例子,CAS state失败进去 acquire

    final void lock() {
        // state不是0 CAS失败进去acuire
        if (compareAndSetState(0, 1))
            setExclusiveOwnerThread(Thread.currentThread());
        else
            acquire(1);
    }
    
  2. 进去acquire方法,此时tryAcquire还是失败,进入addWaiter,注意!!此时的AQS的队列已经变成了哑结点->Node(ThreadB)

    public final void acquire(int arg) {
        // tryAcquire获取锁失败 返回 false取反继续进入addWaiter方法
        if (!tryAcquire(arg) &&
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
            selfInterrupt();
    }
    
  3. addWaiter方法 此时获取tail节点已经不是null而是Node(Thread_B)

    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(ThreadC)追加到队列尾部 返回
            node.prev = pred;
            if (compareAndSetTail(pred, node)) {
                pred.next = node;
                return node;
            }
        }
        enq(node);
        return node;
    }
    
  4. addWaiter方法返回将Node(ThreadC)追加到队列进去acquireQueued方法

    final boolean acquireQueued(final Node node, int arg) {
        boolean failed = true;
        try {
            boolean interrupted = false;
            for (;;) {
                // 此时p为 Node(ThreadB)
                final Node p = node.predecessor();
                // p!=head 此时head还是哑结点
                if (p == head && tryAcquire(arg)) {
                    setHead(node);
                    p.next = null; // help GC
                    failed = false;
                    return interrupted;
                }
                // 此时逻辑跟之前一样 将Node(ThreadB)的waitState改为-1
                // 然后调用LockSupport将线程挂起
                if (shouldParkAfterFailedAcquire(p, node) &&
                    parkAndCheckInterrupt())
                    interrupted = true;
            }
        } finally {
            if (failed)
                cancelAcquire(node);
        }
    }
    

下面开始调用unlock解锁debug

  1. 底层调用了AQS的release方法 首先进去tryRelease方法尝试释放锁 进入ReentrantLock查看

    public final boolean release(int arg) {
        if (tryRelease(arg)) {
            Node h = head;
            if (h != null && h.waitStatus != 0)
                unparkSuccessor(h);
            return true;
        }
        return false;
    }
    
  2. 进入tryRelease

    protected final boolean tryRelease(int releases) {
        // state = 1 -realse 1 = 0
        int c = getState() - releases;
        // 判断当前线程是不是 AQS父类的设置进去的排他线程
        if (Thread.currentThread() != getExclusiveOwnerThread())
            throw new IllegalMonitorStateException();
        boolean free = false;
        if (c == 0) {
            free = true;
            // 设置排他线程为null
            setExclusiveOwnerThread(null);
        }
        // 将state改成0
        setState(c);
        // 返回true
        return free;
    }
    
  3. 返回release方法继续执行,判断头节点是否为空和waitStatus的状态

    public final boolean release(int arg) {
        if (tryRelease(arg)) {
            // 获取头节点 此时为哑结点
            Node h = head;
            // 此时头节点部位空 且 waitStatus为-1 进入unparkSuccessor
            if (h != null && h.waitStatus != 0)
                unparkSuccessor(h);
            return true;
        }
        return false;
    }
    
  4. 进去unparkSuccessor方法

    private void unparkSuccessor(Node node) {
    	// 此时的node是头节点 头节点的waitStatus = -1
        int ws = node.waitStatus;
        // 执行CAS将 ws从-1改成0
        if (ws < 0)
            compareAndSetWaitStatus(node, ws, 0);
    
        /*
             * Thread to unpark is held in successor, which is normally
             * just the next node.  But if cancelled or apparently null,
             * traverse backwards from tail to find the actual
             * non-cancelled successor.
             */
        // 头节点的后一个节点
        Node s = node.next;
        // 此时头节点后继节点不为null不进if
        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;
        }
        // 调用LockSupport 将后继节点的Thread唤醒
        if (s != null)
            LockSupport.unpark(s.thread);
    }
    
  5. 此时ThreadB已经被唤醒… 注意!!!此时要调回到当时被park的地方继续往下执行,就是lock方法中的代码!!!此时需要对任务等待队列的出队操作,此时调回到AQS的acquireQueued方法

        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 因为realse释放了继续往下执行最终进入finally 方法进行队列的出队
                        parkAndCheckInterrupt())
                        interrupted = true;
                }
            } finally {
                if (failed)
                    cancelAcquire(node);
            }
        }
    
  6. 继续进去for循环

    for (;;) {
        final Node p = node.predecessor();
        // 此时p是哑结点 进入tryAcquire继续查询获取锁
        if (p == head && tryAcquire(arg)) {
            setHead(node);
            p.next = null; // help GC
            failed = false;
            return interrupted;
        }
        if (shouldParkAfterFailedAcquire(p, node) &&
            parkAndCheckInterrupt())
            interrupted = true;
    }
    
  7. 进入nonfairTryAcquire方法 CAS成功将当前排他线程改成ThreadB返回for循环继续执行

    final boolean nonfairTryAcquire(int acquires) {
        final Thread current = Thread.currentThread();
        int c = getState();
        // 此时的state因为ThreadA已经释放了 state=0 设置当前排他线程为ThreadB
        // 返回True获取锁成功
        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;
    }
    
  8. for循环继续执行,将Node(ThreadB)变成新的头节点(哑结点)

    for (;;) {
        final Node p = node.predecessor();
        // 此时p是哑结点 进入tryAcquire继续查询获取锁
        if (p == head && tryAcquire(arg)) {
            // 将当前头节点改成ThreadB
            // 将当前Head的Thread改成null AQS 的head指针指向该节点当前指针的pre节点指向null
            // 相当于此时Node(ThreadB)变成了新的哑结点
            setHead(node);
            // 之前的头节点next指针断开与GCRoots的关联让GC可以回收
            p.next = null; // help GC
            failed = false;
            return interrupted;
        }
        if (shouldParkAfterFailedAcquire(p, node) &&
            parkAndCheckInterrupt())
            interrupted = true;
    }
    // setHead方法
    private void setHead(Node node) {
        head = node;
        node.thread = null;
        node.prev = null;
    }
    
  9. 此时realse工作完成

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
城市应急指挥系统是智慧城市建设的重要组成部分,旨在提高城市对突发事件的预防和处置能力。系统背景源于自然灾害和事故灾难频发,如汶川地震和日本大地震等,这些事件造成了巨大的人员伤亡和财产损失。随着城市化进程的加快,应急信息化建设面临信息资源分散、管理标准不统一等问题,需要通过统筹管理和技术创新来解决。 系统的设计思路是通过先进的技术手段,如物联网、射频识别、卫星定位等,构建一个具有强大信息感知和通信能力的网络和平台。这将促进不同部门和层次之间的信息共享、交流和整合,提高城市资源的利用效率,满足城市对各种信息的获取和使用需求。在“十二五”期间,应急信息化工作将依托这些技术,实现动态监控、风险管理、预警以及统一指挥调度。 应急指挥系统的建设目标是实现快速有效的应对各种突发事件,保障人民生命财产安全,减少社会危害和经济损失。系统将包括预测预警、模拟演练、辅助决策、态势分析等功能,以及应急值守、预案管理、GIS应用等基本应用。此外,还包括支撑平台的建设,如接警中心、视频会议、统一通信等基础设施。 系统的实施将涉及到应急网络建设、应急指挥、视频监控、卫星通信等多个方面。通过高度集成的系统,建立统一的信息接收和处理平台,实现多渠道接入和融合指挥调度。此外,还包括应急指挥中心基础平台建设、固定和移动应急指挥通信系统建设,以及应急队伍建设,确保能够迅速响应并有效处置各类突发事件。 项目的意义在于,它不仅是提升灾害监测预报水平和预警能力的重要科技支撑,也是实现预防和减轻重大灾害和事故损失的关键。通过实施城市应急指挥系统,可以加强社会管理和公共服务,构建和谐社会,为打造平安城市提供坚实的基础。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值