可重入锁源码分析

      ReentrantLocks意为可重入锁,也就是获得锁的同一个线程可以多次获得锁而不会阻塞。但是同一时间内锁只能被同一个线程持有,同时ReentrantLock又分为公平锁和非公平锁,但是他们都是通过维护一个节点队列来实现,只不过公平锁每次都取头结点执行,而非公平锁每次可能随机取节点执行。无论那种情况,每当一个线程获得锁之后都会使状态加一,当状态为0时会通知后面排队的节点(或者是在非公平锁中某一个正好合适的线程执行)。



 /**
     * 一个抽象类,是锁同步控制的基础。子类有公平锁和非公平锁两种。
     */
    abstract static class Sync extends AbstractQueuedSynchronizer {
        private static final long serialVersionUID = -5179523762034025860L;

        /**
         * 默认是非公平锁.
         */
        abstract void lock();

        /**
         * 非公平锁尝试获取资源.
         */
        final boolean nonfairTryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            int c = getState();
            if (c == 0) {//状态为0,表示还没有线程获得锁
                if (compareAndSetState(0, acquires)) {//CAS方式设置资源
                    setExclusiveOwnerThread(current);//成功则把当前线程设置为排他线程
                    return true;
                }
            }
            else if (current == getExclusiveOwnerThread()) {//如果线程是已经获得锁的线程
                int nextc = c + acquires;
                if (nextc < 0) // 溢出抛出异常
                    throw new Error("Maximum lock count exceeded");
                setState(nextc);//设置新的状态值
                return true;
            }
            return false;//没获得资源,返回false
        }

        protected final boolean tryRelease(int releases) {//尝试释放资源
            int c = getState() - releases;
            if (Thread.currentThread() != getExclusiveOwnerThread())//线程不一致抛出异常
                throw new IllegalMonitorStateException();
            boolean free = false;
            if (c == 0) {
                free = true;
                setExclusiveOwnerThread(null);
            }
            setState(c);
            return free;//释放成功返回true
        }

        protected final boolean isHeldExclusively() {//当前线程是否是获得了排他属性的线程
            // While we must in general read state before owner,
            // we don't need to do so to check if current thread is owner
            return getExclusiveOwnerThread() == Thread.currentThread();
        }

        final ConditionObject newCondition() {
            return new ConditionObject();
        }

        // Methods relayed from outer class

        final Thread getOwner() {
            return getState() == 0 ? null : getExclusiveOwnerThread();
        }

        final int getHoldCount() {//当前线程锁的次数
            return isHeldExclusively() ? getState() : 0;
        }

        final boolean isLocked() {//判断是否有锁
            return getState() != 0; 
        }

        /**
         * Reconstitutes the instance from a stream (that is, deserializes it).
         */
        private void readObject(java.io.ObjectInputStream s)
            throws java.io.IOException, ClassNotFoundException {
            s.defaultReadObject();
            setState(0); // reset to unlocked state
        }
    }

   Syn的两种实现方式:公平锁和非公平锁。公平锁的语义和非公平锁的语义不同在于,公平锁每次从队列的首部获取等待最久的线程,而非公平锁则每次随机获取线程执行,很可能某一个线程多次获取锁,导致其他线程饥饿。在下面代码中可以看出,非公平锁首先使用CAS设置状态,也就是说如果锁是空就直接占有,然后进入acquire进行处理。而公平锁则直接进入acquire(1).

 /**
     * 非公平锁
     */
    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() {
            if (compareAndSetState(0, 1))//CAS设置当前为0 的时候上锁
                setExclusiveOwnerThread(Thread.currentThread());
            else
                acquire(1);//否则尝试获得锁。
        }

        protected final boolean tryAcquire(int acquires) {
            return nonfairTryAcquire(acquires);
        }
    }

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

        final void lock() {
            acquire(1);
        }

        /**
         * 
         */
        protected final boolean tryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            int c = getState();
            if (c == 0) {
                if (!hasQueuedPredecessors() &&
                    compareAndSetState(0, acquires)) {//没有前驱节点并且CAS设置成功
                    setExclusiveOwnerThread(current);//设置当前线程为独占线程
                    return true;
                }
            }
            else if (current == getExclusiveOwnerThread()) {//这里和非公平锁类似
                int nextc = c + acquires;
                if (nextc < 0)
                    throw new Error("Maximum lock count exceeded");
                setState(nextc);
                return true;
            }
            return false;
        }
    }

acquire中的详细介绍。acquire是AQS类中的方法。其中tryAcquire就是(非)公平锁类下的tryAcquire方法,若没有获得资源那么就继续执行,否则结束此函数。addWaiter用于添加节点,也就是把当前线程对应的节点插入CLH队列的尾部。

 /**
     * 这个方法也就是lock()方法的关键方法。tryAcquire获得资源,返回true,直接结束。若未获取资源,新建一个节点插入队尾,
     *
     * @param arg the acquire argument.  This value is conveyed to
     *        {@link #tryAcquire} but is otherwise uninterpreted and
     *        can represent anything you like.
     */
    public final void acquire(int arg) {
        if (!tryAcquire(arg) &&//获取资源立刻结束
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))//没有被中断过,也结束
            selfInterrupt();
    }
/**
     * 为当前线程和模式创建一个节点,这个也是AQS中的类。
     *
     * @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;//设置pred为尾节点这里尝试一次。
        if (pred != null) {//尾节点不为空则设置新节点前驱为尾节点
            node.prev = pred;
            if (compareAndSetTail(pred, node)) {//CAS原子操作将node设置为新的尾节点
                pred.next = node;
                return node;//pred下一个节点为新的尾节点。并返回新的尾节点
            }
        }
        enq(node);//上面没成功,将节点插入队尾
        return node;
    }
 /**
     *把一个节点插入队尾中
     */
    private Node enq(final Node node) {
        for (;;) {
            Node t = tail;//将尾节点赋值给t
            if (t == null) { // Must initialize
                if (compareAndSetHead(new Node()))//尾节点为空CAS设置新节点为头结点
                    tail = head;
            } else {
                node.prev = t;//node 前置节点设置为t
                if (compareAndSetTail(t, node)) {//CAS操作设置node 为尾节点
                    t.next = node;
                    return t;//返回新的尾节点。
                }
            }
        }
    }
final boolean acquireQueued(final Node node, int arg) {
        boolean failed = true;
        try {
            boolean interrupted = false;//表示当前线程在休眠过程中有没有被中断过
            for (;;) {//循环获取前驱节点,一直到前驱是头结点,并且某个节点获取了资源
                final Node p = node.predecessor();//获得node节点的前驱节点
                if (p == head && tryAcquire(arg)) {//p节点为头结点,当前线程获得了资源
                    setHead(node);//当前节点设置为头结点,取消排队
                    p.next = null; // 把node 设置为null
                    failed = false;
                    return interrupted;//返回是否中断过
                }
                if (shouldParkAfterFailedAcquire(p, node) &&//判断当前线程是否应该阻塞
                    parkAndCheckInterrupt())//阻塞当期线程,在这里执行park操作
                    interrupted = true;
            }
        } finally {
            if (failed)
                cancelAcquire(node);
        }
    }
 /**
     * 检查自己在没有获得资源之后,是不是应该挂起。
     *
     * @param pred node's predecessor holding status
     * @param node the node
     * @return {@code true} if thread should block
     */
    private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
        int ws = pred.waitStatus;//获得前置节点的状态
        if (ws == Node.SIGNAL)
            /*
             * 如果前置节点的状态是sigal,那么就可以返回true,也就是意味着线程可以被阻塞了。
             */
            return true;
        if (ws > 0) {
            /*
             * 如果前置节点的状态是删除状态,那么就一直找到一个正常的状态排在它后面。
             */
            do {
                node.prev = pred = pred.prev;
            } while (pred.waitStatus > 0);
            pred.next = node;
        } else {
            /*
             *如果前驱正常,就把前驱状态设置为sigal
             */
            compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
        }
        return false;
    }


这里简单总结一下lock的过程:

   首先尝试获取资源,如果当前状态为0,表示没有线程占有锁,设置该线程为独占模式,使用CAS设置状态,否则如果当前线程和独占线程是一个线程,修改状态值,否则返回false。

  若获取资源失败,则通过addWaiter方法创建一个节点并放在CLH队列的尾部。

  逐步去执行CLH队列中的线程,当前线程会公平性的阻塞一直到获取锁为止,返回线程在等待的过程中还是否中断过。



unLock方法比较简单,直接release(1)即可 。

    /**
     * Releases in exclusive mode.  Implemented by unblocking one or
     * more threads if {@link #tryRelease} returns true.
     * This method can be used to implement method {@link Lock#unlock}.
     *
     * @param arg the release argument.  This value is conveyed to
     *        {@link #tryRelease} but is otherwise uninterpreted and
     *        can represent anything you like.
     * @return the value returned from {@link #tryRelease}
     */
    public final boolean release(int arg) {
        if (tryRelease(arg)) {//尝试释放锁,其实就是状态减一。
            Node h = head;
            if (h != null && h.waitStatus != 0)
                unparkSuccessor(h);//成功则唤醒后继节点
            return true;
        }
        return false;
    }
    // 线程已被取消
    static final int CANCELLED =  1;
    // 当前线程的后继线程需要被unpark(唤醒)
    // 一般发生情况是:当前线程的后继线程处于阻塞状态,而当前线程被release或cancel掉,因此需要唤醒当前线程的后继线程。
    static final int SIGNAL    = -1;
    // 在Condition休眠状态,在等待Condition唤醒
    static final int CONDITION = -2;
    // (共享锁)其它线程获取到“共享锁”,对应的waitStatus的值
    static final int PROPAGATE = -3;

   protected final boolean tryRelease(int releases) {
            int c = getState() - releases;
            if (Thread.currentThread() != getExclusiveOwnerThread())
                throw new IllegalMonitorStateException();//持锁线程和当前线程不一致抛出异常
            boolean free = false;
            if (c == 0) {//状态为0 说明锁已经不被线程所占有
                free = true;
                setExclusiveOwnerThread(null);//设置占有锁线程为null
            }
            setState(c);//设置锁的状态
            return free;
        }
/**
     * Wakes up node's successor, if one exists.
     *
     * @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;
        if (ws < 0)//获取当前节点状态,若小于0则置状态为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;
            for (Node t = tail; t != null && t != node; t = t.prev)
                if (t.waitStatus <= 0)//从尾部往前找,找到最靠前的有效节点
                    s = t;
        }
        if (s != null)
            LockSupport.unpark(s.thread);//唤醒之
    }
 一次unlock操作需要修改状态位,然后唤醒节点。整个释放操作也是使用unpark()来唤醒队列最前面的节点。其实lock中比较重要的也就是lock和release,它们又和AQS联系紧密,下面会单独谈谈AQS的重要方法。




参考资料:

http://ifeve.com/juc-aqs-reentrantlock/

http://ifeve.com/java-special-troops-aqs/

http://www.cnblogs.com/skywang12345/p/3496147.html#p24

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


猜你喜欢

转载自blog.csdn.net/pb_yan/article/details/80502119