AQS浅显理解

AQS (AbstractQueuedSynchronizer)

AQS
https://www.cnblogs.com/waterystone/p/4920797.html

美团技术AQS
https://tech.meituan.com/2019/12/05/aqs-theory-and-apply.html

1. AQS的数据结构

avatar

  • FIFO双端队列结构(Node结构的双向链表形成, 每一个Node与一个线程进行绑定)
	static final class Node {
        static final Node SHARED = new Node();
        static final Node EXCLUSIVE = null;

        // waitStatus的四种状态
        /*
        	0. 当一个Node被初始化的时候的默认值
			1. CANCLEED:表示线程获取锁的请求已经取消了
			2. SIGNAL:表示唤醒当前节点的后继节点。(eg:当前节点的prev.waitStatus == -1就表示其后继已经被唤醒。)
			3. CONDITION:表示节点在条件等待队列(condition)中,节点线程等待唤醒
			4. PROPAGATE:当前线程处在SHARED情况下,该字段才会使用. 共享模式下,前继结点不仅会唤醒其后继结点,同时也可能会唤醒后继的后继结点。

        */
        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;

        // 下一个节点。 这个是在条件condition队列中。
        // 指向下一个处于CONDITION状态的节点
        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() {    // Used to establish initial head or SHARED marker
        }

        // Node mode表示模式(独占还是共享)
        Node(Thread thread, Node mode) {     // Used by addWaiter
            this.nextWaiter = mode;
            this.thread = thread;
        }

        Node(Thread thread, int waitStatus) { // Used by Condition
            this.waitStatus = waitStatus;
            this.thread = thread;
        }
    }

2. AQS 的主要使用方法

  • 锁与释放锁调用的方法

    • public final void acquire(int arg);// lock()方法调用。
    • public final boolean release(int arg);// unlock()方法调用
  • 针对独占锁

    • protected boolean tryAcquire(int arg);// 需要AQS的子类自己重写。 尝试获取独占锁。 如果获取成功返回true。反之false
    • protected boolean tryRelease(int arg);// 需要AQS的子类自己重写。尝试释放锁。如果获取成功返回true。反之false
  • 针对共享锁

    • protected int tryAcquireShared(int arg);// 尝试获取共享锁,arg表示获取锁的次数。 返回值——负数表示失败;0表示成功,但没有剩余可用资源;正数表示成功,且有剩余资源。
    • protected boolean tryReleaseShared(int arg);// arg为释放锁的次数,尝试释放资源,如果释放后允许唤醒后续等待结点返回True,否则返回False。

AQS的独占模式的使用——————针对ReentrantLock的非公平方式分析

1. lock()方法

  • 主要流程图
  1. lock()总体步骤
    avatar

  2. acquire()
    avatar

  3. tryAcquire()
    avatar

  4. addQueue()
    avatar

    public class ReentrantLock implements Lock, java.io.Serializable {

        
        private final Sync sync;

        // lock.lock()首先调用这个方法
        public void lock() {
            sync.lock();// 这个调用的lock()方法是NonfairSync(Sync的子类)中的lock()方法
        }

        // Sync是一个继承了AQS的抽象类。 主要通过它来重写一些方法。比如tryAcquire()或者tryRelease().
        abstract static class Sync extends AbstractQueuedSynchronizer {
            private static final long serialVersionUID = -5179523762034025860L;

            abstract void lock();

            // tryAcquire()方法实际调用的方法
            final boolean nonfairTryAcquire(int acquires) {
                // 获取当前线程
                final Thread current = Thread.currentThread();

                // 当前的状态。 初始状态是0, 每当获取锁状态+1
                int c = getState();
                if (c == 0) {
                    // 利用CAS来设置state的值
                    if (compareAndSetState(0, acquires)) {
                        // 这个方法是将独占锁的owner(拥有者)设置为当前线程
                        setExclusiveOwnerThread(current);
                        return true;
                    }
                }
                // 判断当前线程是否是 占有锁的线程。 如果是继续下去, 这里应该是针对可重入的使用
                else if (current == getExclusiveOwnerThread()) {
                    // 更改state的值, 每当一个锁使用state+1
                    int nextc = c + acquires;
                    if (nextc < 0) // overflow
                        throw new Error("Maximum lock count exceeded");
                    // 设置state
                    setState(nextc);
                    return true;
                }

                // 上述条件都不满足的话, 就表示获取锁失败
                return false;
            }
        }
        static final class NonfairSync extends Sync {
            private static final long serialVersionUID = 7316153563782823691L;

            // lock()方法实际的调用
            final void lock() {
                // 尝试CAS, 看当前state的值是否为0。 如果是就将state的值更新为1. 然后将当前线程设置为 独占锁的线程
                if (compareAndSetState(0, 1))
                    setExclusiveOwnerThread(Thread.currentThread());
                else
                    // 反之调用acquire()方法, 这是AQS父类中的方法, 见下面AQS
                    acquire(1);
            }

            // AQS中的acquire()中tryAcquire()调用的是该方法
            protected final boolean tryAcquire(int acquires) {
                return nonfairTryAcquire(acquires);
            }
        }

    }

// AQS

    public abstract class AbstractQueuedSynchronizer extends AbstractOwnableSynchronizer implements java.io.Serializable {
        // Node节点的定义,见上面。

        // 构造方法
        protected AbstractQueuedSynchronizer() { }

        // 队列的头部
        private transient volatile Node head;

        // 队列的尾部
        private transient volatile Node tail;

        // 状态值
        private volatile int state;
            // 给定了set和get方法
        }

        // 获取锁调用的方法
        public final void acquire(int arg) {
            // 其中tryAcquire()调用的是子类的tryAcquire()方法.分析是否获取到锁。如果没有获取到锁, 然后进行下面
            // acquireQueued()方法的使用。中间调用addWaiter()方法
            if (!tryAcquire(arg) &&
                acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
                selfInterrupt();
        }

        // 该方法将没有获取到锁线程加入到Node队列中等待
        // 针对独占锁传入的mode就是Node.EXCLUSIVE也就是null
        // 返回值:当前加入队列的节点
        private Node addWaiter(Node mode) {
            Node node = new Node(Thread.currentThread(), mode);
            
            Node pred = tail;
            // 这里表示队列中已经有了元素
            if (pred != null) {
                node.prev = pred;
                // CAS改变tail的值
                if (compareAndSetTail(pred, node)) {
                    pred.next = node;
                    return node;
                }
            }

            // enq(Node node)方法将node节点加入到队列中
            // 这是CAS改变tail的值失败,或者是pred == null(也就是空队列)加入到队列中
            enq(node);
            return node;
        }

        // 这个方法的作用:不断的自旋,尝试来获取锁的使用权
        final boolean acquireQueued(final Node node, int arg) {
            // failed我认为
            boolean failed = true;
            try {
                boolean interrupted = false;
                // 自旋
                for (;;) {
                    // node.predecessor()获取当前节点的前驱节点
                    final Node p = node.predecessor();
                    // 如果p节点是head节点,就表示node节点是该等待队列中的第二个节点。那么让它来尝试获取锁tryAcquire(arg)
                    // 从enq()方法可以看出head节点是一个相当于空节点的东西
                    if (p == head && tryAcquire(arg)) {
                        // 如果获取到了锁, 那么将node当前节点设置为head
                        setHead(node);
                        // 这个是为了帮助GC。 p节点是之前的head。 head.next = null就让head没有使用了
                        p.next = null; // help GC
                        failed = false;
                        return interrupted;
                    }

                    // 如果p不是head节点或者没有获取到锁。执行下面的步骤
                    // shouldParkAfterFailedAcquire(p, node):判断在获取锁失败之后是否应该park, 如果需要park执行parkAndCheckInterrupt()
                    // 如果该线程中断过就设置interrupted=true
                    if (shouldParkAfterFailedAcquire(p, node) &&
                        parkAndCheckInterrupt())
                        interrupted = true;
                }
            // 当当前线程申请锁的请求被取消的话, 就将当前node节点的waistatus置为CANCELLED
            } finally {
                if (failed)
                    cancelAcquire(node);
            }
        }

        // 返回值:true表示需要阻塞;false表示不用park
        /*
            参数值
                * Node pred:node节点的前一个节点
                * Node node当前节点
        */
        private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
            // 前一个节点的状态
            int ws = pred.waitStatus;
            // 如果是SIGNAL表示node节点已经被唤醒了, 但是没获取到锁就park
            if (ws == Node.SIGNAL)
                return true;

            // 如果 pred的waitStatus是 CANCELLED(表示线程获取锁的请求已经取消了)。不断的向前的找状态不是CANCELLED的节点
            if (ws > 0) {
                do {
                    node.prev = pred = pred.prev;
                } while (pred.waitStatus > 0);
                pred.next = node;
            } else {
                // CAS改变ws的值
                compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
            }
            return false;
        }

        // park()当前线程
        // 返回值:该线程是否中断过
        private final boolean parkAndCheckInterrupt() {
            LockSupport.park(this);
            return Thread.interrupted();
        }

        // 将当前节点设置为CANCELLED,从队列中移出。
        private void cancelAcquire(Node node) {
            // Ignore if node doesn't exist
            if (node == null)
                return;

            // 将线程与该node解绑
            node.thread = null;

            // 跳过那些CANCELLED的前驱节点
            Node pred = node.prev;
            while (pred.waitStatus > 0)
                node.prev = pred = pred.prev;

            // node节点前驱的前驱
            Node predNext = pred.next;

            // 将node节点的状态置为CANCELLED
            node.waitStatus = Node.CANCELLED;

            // 如果当前节点node是tail节点。删除之后设置prev节点为新的tail节点
            if (node == tail && compareAndSetTail(node, pred)) {
                compareAndSetNext(pred, predNext, null);
            } else {
                
                int ws;
                // 如果pred也就是当前节点node的前驱节点不是head。分两种情况
                // 1. pred.waitStatus是否为Node.SIGNAL 2. CAS pred的ws是否能够成功
                // 如果两种情况有一个为true那么就判断pred节点对应的线程是都为null
                if (pred != head &&
                    ((ws = pred.waitStatus) == Node.SIGNAL ||
                     (ws <= 0 && compareAndSetWaitStatus(pred, ws, Node.SIGNAL))) &&
                    pred.thread != null) {
                    Node next = node.next;
                    // 这里是利用CAS来将当前节点的前驱结点的next设置为 当前节点的后继节点
                    if (next != null && next.waitStatus <= 0)
                        compareAndSetNext(pred, predNext, next);
                } else {
                    // 如果pred == head, 就尝试唤醒node节点的后继节点
                    unparkSuccessor(node);
                }

                node.next = node; // help GC
            }
        }

        // unpark node节点的后继节点
        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;
                // 从后往前找 找到第一个ws < 0的节点
                for (Node t = tail; t != null && t != node; t = t.prev)
                    if (t.waitStatus <= 0)
                        s = t;
            }
            if (s != null)
                // 唤醒node的后继
                LockSupport.unpark(s.thread);
        }

        // 将节点加入到Node队列中, 这个方法的返回值是当前节点的前一个节点
        // 利用的是CAS的方法进行加入, 不断的循环
        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;
                    }
                }
            }
        }

2. unlock()方法

  • 方法调用顺序流程图
    avatar
    public class ReentrantLock implements Lock, java.io.Serializable {
        public void unlock() {
            sync.release(1);
        }

        abstract static class Sync extends AbstractQueuedSynchronizer {
            // 省略

            // tryRelease()调用这个方法。尝试来释放资源
            // 返回值:true:释放成功
            protected final boolean tryRelease(int releases) {
                int c = getState() - releases;
                if (Thread.currentThread() != getExclusiveOwnerThread())
                    throw new IllegalMonitorStateException();
                boolean free = false;
                // 对于可重入锁,每次调用这个方法 state会--; 直到最后state == 0就释放锁
                if (c == 0) {
                    free = true;
                    // 将锁释放,其拥有者置为null
                    setExclusiveOwnerThread(null);
                }
                // 修改state
                setState(c);
                return free;
            }
        }
    }
    public abstract class AbstractQueuedSynchronizer
    extends AbstractOwnableSynchronizer
    implements java.io.Serializable {
        public final boolean release(int arg) {
            if (tryRelease(arg)) {
                Node h = head;
                if (h != null && h.waitStatus != 0)
                    // unpark(唤醒) head节点的后一个节点。
                    unparkSuccessor(h);
                return true;
            }
            return false;
        }
    }
    
posted @ 2021-05-31 10:12  codeDJH  阅读(96)  评论(0)    收藏  举报