• AQS笔记一 --- 源码分析


    AQS笔记一 ---- 部分源码分析

    AQS(AbstractQueuedSynchronizer) 队列同步器, AQS定义了一套多线程访问共享资源的同步器框架.

    AQS 内部依赖的同步队列(一个FIFO双向队列)来完成同步状态的管理, 当前线程获取同步状态失败时, 同步器会将当前线程以及等待状态等信息构成一个节点(Node) 并将其加入到同步队列, 同时阻塞当前线程, 当同步状态释放时, 会将首节点中的线程唤醒, 使其再次尝试获取同步状态.

    注: 以下源码基于jdk1.8

    一、内部类, Node节点

    Node类, 定义了同步队列中的节点对象, waitStatus等待状态, thread记录线程对象引用;

    static final class Node {
        /** Marker to indicate a node is waiting in shared mode */
        static final Node SHARED = new Node();
        /** Marker to indicate a node is waiting in exclusive mode */
        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; // 等待状态 
        volatile Node prev; // 前继节点
        volatile Node next; // 后继节点
        volatile Thread thread; // 记录线程引用
        // nextWaiter为SHARED时, 表示共享模式, 为EXCLUSIVE时, 表示排他模式
        Node nextWaiter;  
    }
    

    waitStatus取值:

    • CANCELLED: 值为1, 在同步队列中等待的线程等待超时或者被中断, 表示结束状态, 进入该状态后的节点将不会再变化,
    • 0状态, 值为0, 表示初始化状态;
    • SIGNAL: 值为-1, 此状态的节点, 处于等待唤醒状态,当其前继节点的线程释放了同步锁或被取消后, 会通知该后继节点线程执行; 即: 只要前继节点释放锁后, 就会通知标识为SIGNAL状态的后继节点的线程执行;
    • CONDITION: 值为-2, 与Condition相关, 该标识的节点处于等待队列中, 节点的线程等待在Condition上, 其他线程调用Condition的signal()方法后, CONDITION状态的节点将从等待队列转移到同步队列中, 等待获取同于锁;
    • PROPAGATE: 值为-3, 与共享模式相关, 在共享模式中, 该状态标识的节点的线程处于可运行状态;

    AQS在判断状态时, 通过用waitStatus>0表示取消状态, waitStatus< 0表示有效状态

    二、AQS成员变量

    AbstractQueuedSynchronizer中定义偏移量的原因, 是为了其他线程被阻塞的情况下, 通过当前线程调用Unsafe类的方法改变那个线程属性值;

    public abstract class AbstractQueuedSynchronizer
        extends AbstractOwnableSynchronizer
        implements java.io.Serializable 
    {
        private volatile int state; // 同步状态
        private transient volatile Node head; // 同步队列头节点
        private transient volatile Node tail; // 同步队列尾节点
    
        private static final Unsafe unsafe = Unsafe.getUnsafe(); // 获取Unsafe实例
        private static final long stateOffset; // 记录state在AQS类中的偏移量
        private static final long headOffset; // 记录head在AQS类中偏移量
        private static final long tailOffset; // 记录tail在AQS类中偏移量
        private static final long waitStatusOffset; //记录waitStatus在Node类中偏移量
        private static final long nextOffset; // 记录 next在Node类中偏移量
    
        // 初始化那些偏移量值
        static {
            try {
                stateOffset = unsafe.objectFieldOffset
                    (AbstractQueuedSynchronizer.class.getDeclaredField("state"));
                headOffset = unsafe.objectFieldOffset
                    (AbstractQueuedSynchronizer.class.getDeclaredField("head"));
                tailOffset = unsafe.objectFieldOffset
                    (AbstractQueuedSynchronizer.class.getDeclaredField("tail"));
                waitStatusOffset = unsafe.objectFieldOffset
                    (Node.class.getDeclaredField("waitStatus"));
                nextOffset = unsafe.objectFieldOffset
                    (Node.class.getDeclaredField("next"));
            } catch (Exception ex) { throw new Error(ex); }
        }
    }
    

    三、源码分析

    AQS源码分析主要分析排他式同步状态获取释放共享式同步状态获取释放

    • acquire/relese
    • acquireShare/releaseShare

    2.1 排他式同步状态获取释放

    2.1.1 acquire(int arg)

    独占式同步状态获取流程:

    // 尝试获取同步状态, 成功则返回;
    // 失败则将当前线程以排他模式添加到同步队列队尾, 然后进入自旋状态, 若在自旋过程中被中断, 则会调用selfInterrupt()将自己中断掉;
    public final void acquire(int arg) {
        if (!tryAcquire(arg) &&
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) // EXCLUSIVE表示这个节点为排他模式
            selfInterrupt(); // 将当前线程中断
    }
    // 将当前线程中断
    static void selfInterrupt() {
        Thread.currentThread().interrupt();
    }
    
    // 尝试获取同步位, 交由子类实现
    protected boolean tryAcquire(int arg) {
        throw new UnsupportedOperationException();
    }
    
    // 将当前线程和mode包装成一个Node对象node, 然后将node添加到同步队列尾部, 最后返回node;
    private Node addWaiter(Node mode) {
        // 获取当前线程, 将mode的值赋给nextWaiter
        Node node = new Node(Thread.currentThread(), mode); 
        // Try the fast path of enq; backup to full enq on failure
        Node pred = tail;
        // 尾节点不为空, 使用CAS算法将node更新为尾节点, 然后返回node
        if (pred != null) {
            node.prev = pred;
            if (compareAndSetTail(pred, node)) {
                pred.next = node;
                return node;
            }
        }
        enq(node); // 尾节点为空, 调用enq方法将node添加到队列尾部
        return node;
    }
    
    // 若队列尾节点为空, 即队列为空, 则创建一个无状态的Node对象作为尾节点, 然后将node添加到同步队列尾部
    private Node enq(final Node node) {
        for (;;) { // 失败重试
            Node t = tail;
            // 尾节点为空, 创建一个无状态的Node对象, 初始化队列
            if (t == null) { // Must initialize
                if (compareAndSetHead(new Node()))
                    tail = head;
            } else { // 然后把node节点添加到队列尾部, 并返回
                node.prev = t;
                if (compareAndSetTail(t, node)) {
                    t.next = node;
                    return t;
                }
            }
        }
    }
    
    // 使用unsafe类的compareAndSwapObject将tail更新为update
    private final boolean compareAndSetTail(Node expect, Node update) {
        return unsafe.compareAndSwapObject(this, tailOffset, expect, update);
    }
    
    // 自旋请求同步状态, 若自旋过程中被中断, 则返回true, 否则返回false; 
    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)) { // 若前继节点为头结点, 并且当前线程(即:node保存的线程)获取到同步状态
                    setHead(node); // 将自己设置为头结点
                    p.next = null; // help GC
                    failed = false;
                    return interrupted; // 返回在自旋过程中是否被中断
                }
                // 若node的前继节点为等待唤醒状态(SIGNAL), 且 等待唤醒过程中被中断, interrupted才会被设置为true
                if (shouldParkAfterFailedAcquire(p, node) &&
                    parkAndCheckInterrupt())
                    interrupted = true;
            }
        } finally {
            if (failed)
                cancelAcquire(node);
        }
    }
    
    // 若前继节点状态为等待唤醒状态, 返回true
    // 若前继节点状态大于0, 则删除状态大于0的前继节点; 若前继节点状态为其他状态, 则将其状态设置为等待唤醒状态; 返回false
    private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
        int ws = pred.waitStatus;
        if (ws == Node.SIGNAL) // node的前继节点处于等待唤醒状态, 返回true
            /*
             * This node has already set status asking a release
             * to signal it, so it can safely park.
             */
            return true;
        if (ws > 0) {
            /*
             * Predecessor was cancelled. Skip over predecessors and
             * indicate retry.
             */
            do {
                // node前继节点状态大于0, 表示前继节点线程已经运行结束或者被中断
                // 将状态大于0的前继节点删除
                node.prev = pred = pred.prev;
            } while (pred.waitStatus > 0);
            pred.next = node;
        } else {
            /*
             * waitStatus must be 0 or PROPAGATE.  Indicate that we
             * need a signal, but don't park yet.  Caller will need to
             * retry to make sure it cannot acquire before parking.
             */
            // 前继节点状态为其他状态, 比如: 0, -2(CONDITION), -3(PROPAGATE), 将node状态设置为等待唤醒状态
            compareAndSetWaitStatus(pred, ws, Node.SIGNAL); 
        }
        return false;
    }
    
    // 使当前线程进入等待, 等待过程中被LockSupport.unpark()唤醒, 或者 被其他线程中断后, 才会继续向下运行
    // 返回当前线程中断状态, 并且刷新中断状态
    private final boolean parkAndCheckInterrupt() {
        LockSupport.park(this); // 当前线程进入等待
        return Thread.interrupted();
    }
    
    // 取消node节点获取资源, 将node状态设置为1(CANCELLED), 如果头结点不为空(...), 则将node节点从同步队列中删除;
    // 头结点为空, 则唤醒下一个后继节点
    private void cancelAcquire(Node node) {
        // Ignore if node doesn't exist
        if (node == null)
            return;
    
        node.thread = null;
    
        // Skip cancelled predecessors
        Node pred = node.prev;
        while (pred.waitStatus > 0)
            node.prev = pred = pred.prev;
    
        // predNext is the apparent node to unsplice. CASes below will
        // fail if not, in which case, we lost race vs another cancel
        // or signal, so no further action is necessary.
        Node predNext = pred.next;
    
        // Can use unconditional write instead of CAS here.
        // After this atomic step, other Nodes can skip past us.
        // Before, we are free of interference from other threads.
        node.waitStatus = Node.CANCELLED;
    
        // If we are the tail, remove ourselves.
        if (node == tail && compareAndSetTail(node, pred)) {
            compareAndSetNext(pred, predNext, null);
        } else {
            // If successor needs signal, try to set pred's next-link
            // so it will get one. Otherwise wake it up to propagate.
            int ws;
            if (pred != head &&
                ((ws = pred.waitStatus) == Node.SIGNAL ||
                 (ws <= 0 && compareAndSetWaitStatus(pred, ws, Node.SIGNAL))) &&
                pred.thread != null) { // 删除node节点的条件
                Node next = node.next;
                if (next != null && next.waitStatus <= 0)
                    compareAndSetNext(pred, predNext, next);
            } else {
                unparkSuccessor(node);
            }
    
            node.next = node; // help GC
        }
    }
    

    2.1.2 relese(int arg)

    // 释放同步状态, 释放成功则唤醒下一个后继节点, 返回true;  释放失败返回false
    public final boolean release(int arg) {
        if (tryRelease(arg)) { // 尝试释放同步状态
            Node h = head; // 获取队列头结点
            // 头结点不为空, 且状态不为0, 唤醒下一个同步节点
            if (h != null && h.waitStatus != 0) 
                unparkSuccessor(h);
            return true;
        }
        return false; 
    }
    protected boolean tryRelease(int arg) {
        throw new UnsupportedOperationException();
    }
    
    // 唤醒下一个后继节点
    private void unparkSuccessor(Node node) {
        /*
         * If status is negative (i.e., possibly needing signal) try
         * to clear in anticipation of signalling.  It is OK if this
         * fails or if status is changed by waiting thread.
         */
        int ws = node.waitStatus; // 获取节点等待状态
        // 将其状态更新为0, 等待状态小于0表示为有效状态, 大于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.
         */
        // 若后继节点为空 或者后继节点状态大于0, 则从尾节点向前遍历, 找到最前面状态小于等于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;
        }
        // s为从tail向前遍历最前面的状态小于等于0的节点
        if (s != null)
            LockSupport.unpark(s.thread);  // s 不为null, 则将 s节点中存放的线程唤醒
    }
    

    2.2 共享式同步状态获取释放

    2.1.1 acquireShared(int arg)

    // 尝试获取同步状态小于0, 
    public final void acquireShared(int arg) {
        if (tryAcquireShared(arg) < 0)
            doAcquireShared(arg);
    }
    // 由子类实现, 共享方式尝试获取共享状态
    protected int tryAcquireShared(int arg) {
        throw new UnsupportedOperationException();
    }
    
    // 自旋获取同步状态
    private void doAcquireShared(int arg) {
        final Node node = addWaiter(Node.SHARED); // 以共享模式将当前线程添加到队尾
        boolean failed = true; // 是否获取失败标识
        try {
            boolean interrupted = false; // 记录获取共享状态过程中是否被中断过
            for (;;) {
                // 获取前继节点, 若前继节点为null, 抛出异常
                final Node p = node.predecessor(); 
                if (p == head) {
                    int r = tryAcquireShared(arg); // 当前线程尝试获取同步状态, 返回获取的资源数
                    if (r >= 0) {
                        setHeadAndPropagate(node, r);
                        p.next = null; // help GC
                        if (interrupted)
                            selfInterrupt();
                        failed = false;
                        return;
                    }
                }
                if (shouldParkAfterFailedAcquire(p, node) &&
                    parkAndCheckInterrupt())
                    interrupted = true;
            }
        } finally {
            if (failed)
                cancelAcquire(node);
        }
    }
    
    // 将当前线程和mode模式包装成Node节点, 添加到同步队列尾部
    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;
        // 若队尾节点不为null, 使用CAS操作, 将node更新到队尾, 返回node
        if (pred != null) {
            node.prev = pred;
            if (compareAndSetTail(pred, node)) {
                pred.next = node;
                return node;
            }
        }
        enq(node);
        return node;
    }
    
    // 若同步队列为null, 则初始化同步队列, 然后将node添加到队列尾部; 
    // 若同步队列不为null, 则将node节点添加到队列尾部
    private Node enq(final Node node) {
        for (;;) {
            Node t = tail;
            // 若队列为空, 初始化队列, 
            if (t == null) { // Must initialize
                if (compareAndSetHead(new Node())) // 通过CAS操作设置队列头部
                    tail = head; // 将头部节点赋值给尾部
            } else {
                node.prev = t;
                if (compareAndSetTail(t, node)) {
                    t.next = node;
                    return t;
                }
            }
        }
    }
    
    // 
    private void setHeadAndPropagate(Node node, int propagate) {
        Node h = head; // Record old head for check below
        setHead(node); // 将node节点的引用赋给head节点
        /*
         * Try to signal next queued node if:
         *   Propagation was indicated by caller,
         *     or was recorded (as h.waitStatus either before
         *     or after setHead) by a previous operation
         *     (note: this uses sign-check of waitStatus because
         *      PROPAGATE status may transition to SIGNAL.)
         * and
         *   The next node is waiting in shared mode,
         *     or we don't know, because it appears null
         *
         * The conservatism in both of these checks may cause
         * unnecessary wake-ups, but only when there are multiple
         * racing acquires/releases, so most need signals now or soon
         * anyway.
         */
        // 获取的资源数大于0, 或者之前的头结点为null, 或者之前的头结点状态小于0,
        // 或者当前的头结点为null, 或者当前头结点状态小于0, 
        // 若下一个后继节点不为空, 或者下一个后继节点为共享模式节点, 则唤醒下一个节点
        if (propagate > 0 || h == null || h.waitStatus < 0 ||
            (h = head) == null || h.waitStatus < 0) {
            Node s = node.next;
            if (s == null || s.isShared())
                doReleaseShared();
        }
    }
    // 若头结点状态不为0, 则将头结点状态置为PROPAGATE,成功则返回,  失败重试;
    // 若头结点状态为SIGNAL, 则将头结点状态置为0, 成功则唤醒下一后继节点, 然后返回 失败则重试;
    // 失败重试之后头结点可能就已经不再是之前的头结点了
    private void doReleaseShared() {
        /*
         * Ensure that a release propagates, even if there are other
         * in-progress acquires/releases.  This proceeds in the usual
         * way of trying to unparkSuccessor of head if it needs
         * signal. But if it does not, status is set to PROPAGATE to
         * ensure that upon release, propagation continues.
         * Additionally, we must loop in case a new node is added
         * while we are doing this. Also, unlike other uses of
         * unparkSuccessor, we need to know if CAS to reset status
         * fails, if so rechecking.
         */
        for (;;) {
            Node h = head;
            if (h != null && h != tail) {
                int ws = h.waitStatus;
                if (ws == Node.SIGNAL) { //头结点状态 Node.SIGNAL值为-1, 表示该节点处于等待唤醒状态
                    if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0)) // 将等待唤醒的节点状态设置为0失败, 失败重试
                        continue;            // loop to recheck cases
                    unparkSuccessor(h); // 唤醒下一个后继节点
                }
                else if (ws == 0 &&
                         !compareAndSetWaitStatus(h, 0, Node.PROPAGATE)) // 头结点状态为0, 将其设置为-3(PROPAGATE), 表示该节点线程处于课运行状态, 失败重试
                    continue;                // loop on failed CAS
            }
            if (h == head)                   // loop if head changed
                break;  // 退出循环
        }
    }
    
    // 唤醒下一个后继节点
    private void unparkSuccessor(Node node) {
        /*
         * If status is negative (i.e., possibly needing signal) try
         * to clear in anticipation of signalling.  It is OK if this
         * fails or if status is changed by waiting thread.
         */
        int ws = node.waitStatus;
        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;
        if (s == null || s.waitStatus > 0) {
            s = null;
            // 从尾节点向前遍历, 找到最后一个不为null, 且状态小于等于0的节点
            for (Node t = tail; t != null && t != node; t = t.prev)
                if (t.waitStatus <= 0)
                    s = t;
        }
        if (s != null)
            LockSupport.unpark(s.thread); // 唤醒
    }
    

    2.1.2 releaseShared(int arg)

    // 尝试释放同步状态, 释放成功则执行doReleaseShared
    // 失败则返回false
    public final boolean releaseShared(int arg) {
        if (tryReleaseShared(arg)) {
            doReleaseShared();
            return true;
        }
        return false;
    }
    
    // 若head节点为等待唤醒状态, 则将其状态设置为0, 然后唤醒下一个后继节点, 返回
    // 若head节点状态为0, 则将其状态设置为-3, 线程可运行状态, 返回
    private void doReleaseShared() {
        /*
         * Ensure that a release propagates, even if there are other
         * in-progress acquires/releases.  This proceeds in the usual
         * way of trying to unparkSuccessor of head if it needs
         * signal. But if it does not, status is set to PROPAGATE to
         * ensure that upon release, propagation continues.
         * Additionally, we must loop in case a new node is added
         * while we are doing this. Also, unlike other uses of
         * unparkSuccessor, we need to know if CAS to reset status
         * fails, if so rechecking.
         */
        for (;;) {
            Node h = head;
            if (h != null && h != tail) {
                int ws = h.waitStatus;
                if (ws == Node.SIGNAL) {
                    if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
                        continue;            // loop to recheck cases
                    unparkSuccessor(h); // 唤醒下一个节点
                }
                else if (ws == 0 &&
                         !compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
                    continue;                // loop on failed CAS
            }
            if (h == head)                   // loop if head changed
                break;
        }
    }
    

    2.3 排他式超时(纳秒为单位)获取同步状态

    nanosTimeout小于1000纳秒采用自旋实现是因为时间非常短, 很难做到精确控制, 因此不会让其进入超时等待, 而是进入自旋状态;

    // 超时获取同步状态, 单位为纳秒, 若超时时间小于等于spinForTimeoutThreshold(1000纳秒), 则会进入自旋而不是进入等待;
    public final boolean tryAcquireNanos(long arg, long nanosTimeout)
            throws InterruptedException {
        if (Thread.interrupted())
            throw new InterruptedException();
        return tryAcquire(arg) ||
            doAcquireNanos(arg, nanosTimeout);
    }
    
    private boolean doAcquireNanos(long arg, long nanosTimeout)
            throws InterruptedException {
        if (nanosTimeout <= 0L)
            return false;
        final long deadline = System.nanoTime() + nanosTimeout;
        final Node node = addWaiter(Node.EXCLUSIVE);
        boolean failed = true;
        try {
            for (;;) {
                final Node p = node.predecessor();
                if (p == head && tryAcquire(arg)) {
                    setHead(node);
                    p.next = null; // help GC
                    failed = false;
                    return true;
                }
                nanosTimeout = deadline - System.nanoTime();
                if (nanosTimeout <= 0L)
                    return false;
                if (shouldParkAfterFailedAcquire(p, node) &&
                    nanosTimeout > spinForTimeoutThreshold) // 只有超时时间大于spinForTimeoutThreshold才会进行等待
                    LockSupport.parkNanos(this, nanosTimeout);
                if (Thread.interrupted())
                    throw new InterruptedException();
            }
        } finally {
            if (failed)
                cancelAcquire(node);
        }
    }
    

    参考

  • 相关阅读:
    Docker安装mysql
    解决SpringMVC+Thymeleaf中文乱码
    Web API 自动生成接口文档
    .Net Core 定时任务TimeJob
    使用 FTP 迁移 SQL Server 数据_迁移数据_快速入门(SQL Server)_云数据库 RDS 版-阿里云
    SQLServer · 最佳实践 · 如何将SQL Server 2012降级到2008 R2-博客-云栖社区-阿里云
    PNG文件转png8
    实战ASP.NET访问共享文件夹(含详细操作步骤)
    MVC JsonResult
    你必须知道的EF知识和经验
  • 原文地址:https://www.cnblogs.com/jxkun/p/9369322.html
Copyright © 2020-2023  润新知