AQS和ReentrantLock源码学习

1:AQS设计思想

  一言以蔽之,AQS的设计思想就是,获取同步资源的成功的线程能够继续运行,而获取同步资源失败的线程要进去等待队列去等待。

1.1:内部节点类介绍

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;

        /** waitStatus value to indicate thread has cancelled */
        //代表当前节点的线程会被取消或者中断了
        static final int CANCELLED =  1;
        /** waitStatus value to indicate successor's thread needs unparking */
        //该状态,暗示当前线程节点的下一个节点需要在当前节点释放锁后,要unpark
        static final int SIGNAL    = -1;
        /** waitStatus value to indicate thread is waiting on condition */
        //代表当前节点时处于condition的队列中,这是因为condition接口内部的等待队列的节点时是
        //复用AQS内部node类,所以增加了该状态代表线程节点是处于condition队列中。
        static final int CONDITION = -2;
        /**
         * waitStatus value to indicate the next acquireShared should
         * unconditionally propagate
         */
        static final int PROPAGATE = -3;

        /**
         * Status field, taking on only the values:
         *   SIGNAL:     The successor of this node is (or will soon be)
         *               blocked (via park), so the current node must
         *               unpark its successor when it releases or
         *               cancels. To avoid races, acquire methods must
         *               first indicate they need a signal,
         *               then retry the atomic acquire, and then,
         *               on failure, block.
         *   CANCELLED:  This node is cancelled due to timeout or interrupt.
         *               Nodes never leave this state. In particular,
         *               a thread with cancelled node never again blocks.
         *   CONDITION:  This node is currently on a condition queue.
         *               It will not be used as a sync queue node
         *               until transferred, at which time the status
         *               will be set to 0. (Use of this value here has
         *               nothing to do with the other uses of the
         *               field, but simplifies mechanics.)
         *   PROPAGATE:  A releaseShared should be propagated to other
         *               nodes. This is set (for head node only) in
         *               doReleaseShared to ensure propagation
         *               continues, even if other operations have
         *               since intervened.
         *   0:          None of the above
         *
         * The values are arranged numerically to simplify use.
         * Non-negative values mean that a node doesn't need to
         * signal. So, most code doesn't need to check for particular
         * values, just for sign.
         *
         * The field is initialized to 0 for normal sync nodes, and
         * CONDITION for condition nodes.  It is modified using CAS
         * (or when possible, unconditional volatile writes).
         */
        //代表当前节点状态的int变量。
        volatile int waitStatus;

        /**
         * Link to predecessor,指向前驱节点
         */
        volatile Node prev;

        /**
         * Link to the successor node,指向后继节点
         */
        volatile Node next;

        /**
         * The thread that enqueued this node.  Initialized on
         * construction and nulled out after use.
         */
        volatile Thread thread;

        /**
         * Link to next node waiting on condition,
         *如果当前线程节点被在condition接口的队列上,则该nextWaiter指向condition队列中的后继节点
         */
        Node nextWaiter;

        /**
         * Returns true if node is waiting in shared mode.
         */
        final boolean isShared() {
            return nextWaiter == SHARED;
        }

        /**
         * Returns previous node, or throws NullPointerException if null.
         * Use when predecessor cannot be null.  The null check could
         * be elided, but is present to help the VM.
         *
         * @return the predecessor of this node
         */
        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(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;
        }
    }

1.2:插入等待队列方法介绍

    //加入等待队列的方法
    /**
     * Inserts node into queue, initializing if necessary. See picture above.
     * @param node the node to insert
     * @return node's predecessor
     */
    private Node enq(final Node node) {
        for (;;) {//死循环
            Node t = tail;//取出当前队列的尾部
            if (t == null) { // Must initialize,如果队列为null,则新建一个线程为null的节点,
                if (compareAndSetHead(new Node()))
                    tail = head;
            } else {//如果不为空,则将待插入节点前驱指向尾部,
                node.prev = t;
                //通过CAS算法判断t是不是依然为尾部,因为该方法没有被同步,
                //有可能被其他线程串改了,如果依然为尾部即tail引用和t引用指向同一个对象
                //则将交换使得tail指向新的node。然后将t的后继为当前新的尾部,
                //因为此时t指向的为tail(当前node被更新了tail了)的前一个节点。
                if (compareAndSetTail(t, node)) {
                    t.next = node;
                    return t;
                }
                //如果CAS操作不成功,则循环继续尝试插入。
            }
        }
    }

    /**
     * Creates and enqueues node for current thread and given mode.
     *
     * @param mode Node.EXCLUSIVE for exclusive, Node.SHARED for shared
     * @return the new 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;
        //逻辑与上面类是。先尝试简单的直接插入,如果成功则好,
        //如果不成功在使用enq方法直到成功。
        if (pred != null) {
            node.prev = pred;
            if (compareAndSetTail(pred, node)) {
                pred.next = node;
                return node;
            }
        }
        enq(node);
        return node;
    }

1.3:唤醒后继节点的方法

     /**
     * Wakes up node's successor, if one exists.
     *唤醒当前节点的后继节点,如果后继节点为null,或者状态waitStatus大于0
     *则后tail开始向前遍历,找到里当前节点最近的且不为null且waitStatue大于0的后继节点,
     *然后唤醒
     * @param node the node
     */
    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;
        //如果当前节点的ws小于0,值置0
        //因为,当前节点计算unpark后继节点,代表当前节点已经释放锁了,
        //已经要出等待队列了。所以要置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,或者waitStatus大于0待变被中断或者取消状态,
        //则也不能unpark
        if (s == null || s.waitStatus > 0) {
            s = null;
            //从尾部tail向前遍历,直到找到找到不为null,且waitStats小于0的节点为止
            for (Node t = tail; t != null && t != node; t = t.prev)
                if (t.waitStatus <= 0)
                    s = t;
        }
        //唤醒unpark前面找到的节点。
        if (s != null)
            LockSupport.unpark(s.thread);
    }

2:ReentrantLock源码分析

  ReentrantLock是实现了AQS机制的一种可重入锁,这种锁公平和非公平锁两种模式。一言以蔽之单个线程或者多线程交替执行的时候和AQS的队列无关的,在虚拟机内部就解决同步问题。当竞争执行的时候才会在队列等待,使用park和unpark在操作系统级别解决同步问题。其实,ReentrantLock并发机制和操作系统的PV机制原理相同。都需要一个整形的信号量,一个等待队列,以及原子性的P操作和V操作。只不过PV操作原子性由OS保证。而AQS的原子性由CAS去保证。

2.1:公平锁和非公平锁的区别

//公平锁类   
/**
 * Sync object for fair locks
 */
static final class FairSync extends Sync {
    private static final long serialVersionUID = -3000897897090466540L;

    //公平锁里的lock方法,执行acquire(1)方法,
    final void lock() {
        acquire(1);
    }

    /**
     * Fair version of tryAcquire.  Don't grant access unless
     * recursive call or no waiters or is first.
     */
    //在acquire的方法里使用模板模式,在调用各自类的tryAcquire(1)方法
    protected final boolean tryAcquire(int acquires) {
        //获取当前线程
        final Thread current = Thread.currentThread();
        //获取当前锁的状态即AQS的state
        int c = getState();
        //如果c==0,意味着锁处于自由状态,即同步资源处于可使用抓状态
        if (c == 0) {
            //进入hasQueuedPredecessors()方法,当该方法返回fasle时
            //才会继续compareAndSetState(0, acquires)方法。
            //进入hasQueuedPredecessors()方法看看,如果该方法返回false
            //达表不存在已经等待时间更长的线程节点,则会继续判断compareAndSetState(0, acquires:1)
            //该方法CAS操作如果c还=0,则state置1,代表当前线程获取锁成功,然后设置拥有线程
            //并return true。
            //这里体现的是公平锁特性,因为虽然当前同步资源为自由状态,
            //但是我们还要判断是不是之前已经有线程在等待了,如果没有才能获取,
            //否则返回false,进入等待队列等待。
            if (!hasQueuedPredecessors() &&
                compareAndSetState(0, acquires)) {
                setExclusiveOwnerThread(current);
                return true;
            }
        }
        //如果当前同步资源处于锁定状态,是不是一定不能获取锁尼?
        //不是的!因为我们是可重入锁,当当前线程与拥有锁的线程相等,也返回true,线程继续运行
        else if (current == getExclusiveOwnerThread()) {
            //可重入锁,增加state值
            int nextc = c + acquires;
            if (nextc < 0)
                throw new Error("Maximum lock count exceeded");
            setState(nextc);
            return true;
        }
        return false;
    }
}



//非公平锁类
/**
 * Sync object for non-fair locks
 */
static final class NonfairSync extends Sync {
    private static final long serialVersionUID = 7316153563782823691L;

    /**
     * Performs lock.  Try immediate barge, backing up to normal
     * acquire on failure.
     */
    final void lock() {
        //非公平锁,上就直接尝试把state通过CAS算法置1,
        if (compareAndSetState(0, 1))
            //如果CAS成功,代表此时同步资源处于自由状态
            //则直接将当前线程作为同步资源的拥有者。
            setExclusiveOwnerThread(Thread.currentThread());
        else
            //如果不成功,在acqure(1),我们进去AQS的acquire看看
            acquire(1);
    }
    //直接调用父类Sync的里面方法的非公平锁方法
    protected final boolean tryAcquire(int acquires) {
        return nonfairTryAcquire(acquires);
    }


}


//这是公平锁和非公平锁的父类Sync的父类AQS里面的acquire方法。
//该方案通过模板模式,在调用子类的tryAcquire方法来实现公平锁和非公平锁的功能
//我们可以看到tryAcquire里面在公平锁和非公平锁的实现不同
public final void acquire(int arg) {
    //两个条件相与,第一个条件为tryAcquire(arg),我们返回锁内部的tryAcquire看看
    //当tryAcquire返回true,代表已经获取锁了,则当前条件不成立,直接从acqure中返回
    //如果返回false,则待变着当前线程要被加入等待队列,阻塞。则后一个条件要执行,将当前线程加入等待队列
    //同时park当前线程。
    if (!tryAcquire(arg) &&
        acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
        selfInterrupt();
}

//这也是来自AQS内部方法:主要功能是判断是不是还有等待时间更长的线程节点存在于队列中
/**
 * Queries whether any threads have been waiting to acquire longer
 * than the current thread.
 * @return {@code true} if there is a queued thread preceding the
 *         current thread, and {@code false} if the current thread
 *         is at the head of the queue or the queue is empty
 *如果,存在等待时间更长的线程节点,则返回true;如果当前线程为head或者等待队列为空,返回false
 * @since 1.7
 */
public final boolean hasQueuedPredecessors() {
    // The correctness of this depends on head being initialized
    // before tail and on head.next being accurate if the current
    // thread is first in queue.
    Node t = tail; // Read fields in reverse initialization order
    Node h = head;
    Node s;
    return h != t &&
        ((s = h.next) == null || s.thread != Thread.currentThread());
}


//这是非公平锁的父类Sync里面实现的nonfairTryAcquire方法
/**
 * Performs non-fair tryLock.  tryAcquire is implemented in
 * subclasses, but both need nonfair try for trylock method.
 */
final boolean nonfairTryAcquire(int acquires) {
    //获取当前线程,
    final Thread current = Thread.currentThread();
    //获取当前同步资源状态
    int c = getState();
    //如果处于自由状态
    if (c == 0) {
        //通过CAS算法直接置1,同时修改当前锁的持有线程
        //没有要不要再继续判断有没有已经处于等待队列的线程的步骤了
        //这也是非公平体现的地方,直接获取锁,不管是不是等待队列已经有等待等长时间的线程了
        if (compareAndSetState(0, acquires)) {
            setExclusiveOwnerThread(current);
            //并放回true
            return true;
        }
    }
    //不为0,判断是不是已经获取锁的线程,逻辑和公平锁类似
    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;
}

  最主要的区别就是:公平锁在判断同步资源处于自由状态后,还需要在进行是不是队列中已经存在等待时间更长的线程,不存在才能进行CAS操作,获取锁。而非公平锁,只要当前同步资源处于自由状态,则立马进行CAS操作尝试获取锁,不管是不是还有没有线程等待等待更久了。

 

 

流程图如上:转载来自https://blog.csdn.net/java_lyvee/article/details/98966684

3:总结

   ReentrantLock是利用AQS机制来实现的一种可重入,公平和非公平两种方法的锁。当在单线程或是或线程交替执行时,不会通过等待队列,直接再JVM层面就保证运行;如果出现了多线程竞争执行,则通过内部的等待队列来管理线程,保证线程的同步。

posted @ 2020-04-13 11:41  大朱123  阅读(196)  评论(0编辑  收藏  举报