ReentrantLock和Condition源码解析

Java除了使用关键字synchronized外,还可以使用ReentrantLock实现独占锁的功能。而且ReentrantLock相比synchronized而言功能更加丰富,使用起来更为灵活,也更适合复杂的并发场景。这篇文章主要是从源码的角度来分析一下ReentrantLock。

ReentrantLock

一、加锁

代码如下:

ReentrantLock reentrantLock = new ReentrantLock();
reentrantLock.lock();

先看创建ReentrantLock对象,ReentrantLock有公平锁和非公平锁两种方式,默认是非公平锁。

    public ReentrantLock() {
        sync = new NonfairSync();
    }

进入NonfairSync类的lock方法。

   static final class NonfairSync extends Sync {
        private static final long serialVersionUID = 7316153563782823691L;

    
        final void lock() {
            if (compareAndSetState(0, 1))
                setExclusiveOwnerThread(Thread.currentThread());
            else
                acquire(1);
        }

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

通过CAS将state由0设置为1,同一时刻多线程只能有一个设置成功,并将线程设置为当前线程。失败的会进入acquire(1)方法。

    public final void acquire(int arg) {
        if (!tryAcquire(arg) &&
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
            selfInterrupt();
    }

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

    final boolean nonfairTryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            int c = getState();
            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;
        }

tryAcquire方法会尝试再次获取一次,如果成功则返回true。否则会判断当前线程是否等于ReentrantLock中的线程,如果相等,代表已获得锁,需要再次进入,相当于重入锁,并把数值+1。

如果获取失败,会通过acquireQueued(addWaiter(Node.EXCLUSIVE), arg)方法尝试加入到等待队列。

   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)) {
                pred.next = node;
                return node;
            }
        }
        //如果不存在前置节点,进入enq方法
        enq(node);
        return node;
    }

   private Node enq(final Node node) {
        for (;;) {
            Node t = tail;
            //如果tail等于null,新创建head和tail节点
            if (t == null) { // Must initialize
                if (compareAndSetHead(new Node()))
                    tail = head;
            } else {
                //设置head为新增节点的前置节点,并设置tail为新增节点
                node.prev = t;
                if (compareAndSetTail(t, node)) {
                    t.next = node;
                    return t;
                }
            }
        }
    }

 接着进入acquireQueued方法

    final boolean acquireQueued(final Node node, int arg) {
        boolean failed = true;
        try {
            boolean interrupted = false;
            for (;;) {
                //获取当前节点的前置节点
                final Node p = node.predecessor();
                //如果前置节点等于head,尝试再次获取
                if (p == head && tryAcquire(arg)) {
                    setHead(node);
                    p.next = null; // help GC
                    failed = false;
                    return interrupted;
                }
                //如果获取失败,执行shouldParkAfterFailedAcquire方法
                if (shouldParkAfterFailedAcquire(p, node) &&
                    parkAndCheckInterrupt())
                    interrupted = true;
            }
        } finally {
            if (failed)
                cancelAcquire(node);
        }
    }

首先会获取当前节点的前置节点,判断前置节点是否为head节点。比如有线程A和线程B,如果线程A执行lock方法获取到锁,则B线程会创建Node对象,B线程Node对象的前置节点是head,之后尝试获取锁。如果获取成功,将自己设置为Head节点。如果失败,会进入shouldParkAfterFailedAcquire(p, node)方法。

   private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
        int ws = pred.waitStatus;
        //如果ws等于Node.SIGNAL(-1),则返回true应该等待
        if (ws == Node.SIGNAL)
            return true;
        if (ws > 0) {
            do {
                node.prev = pred = pred.prev;
            } while (pred.waitStatus > 0);
            pred.next = node;
        } else {
           //初始节点ws等于0,将0置为Node.SIGNAL(-1)状态,等待获取
            compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
        }
        return false;
    }

Node节点有四种状态,1-取消,0-初始化,-1 - 等待, -2 - Condition状态

第一次进入shouldParkAfterFailedAcquire方法后,会将node节点的waitStatus状态由0置为-1。返回false。之后外面会for循环再次进入,当node节点状态为-1时,返回true。之后进入parkAndCheckInterrupt方法。

    private final boolean parkAndCheckInterrupt() {
        LockSupport.park(this);
        return Thread.interrupted();
    }

parkAndCheckInterrupt方法比较简单,通过LockSupport.park方法阻塞线程。

二、解锁

    public void unlock() {
        sync.release(1);
    }

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

线程A获取锁之后,调用unlock方法。会调用tryRelease方法。

        protected final boolean tryRelease(int releases) {
            //获取ReentrantLock的state数值,减去传入的1
            int c = getState() - releases;
            if (Thread.currentThread() != getExclusiveOwnerThread())
                throw new IllegalMonitorStateException();
            boolean free = false;
            //为0则代表释放锁
            if (c == 0) {
                free = true;
                setExclusiveOwnerThread(null);
            }
            //否则设置state数值,也就是我们lock几次,就要调用unlock几次
            setState(c);
            return free;
        }

tryRelease方法会获取ReentrantLock的state数值,减去传入的1。如果减为0,则代表释放锁。否则设置state数值,也就是我们lock几次,就要调用unlock几次。释放后会返回true。调用unparkSuccessor方法。

   private void unparkSuccessor(Node node) {
        //获取head节点状态,如果为-1(等待),置为初始化
        int ws = node.waitStatus;
        if (ws < 0)
            compareAndSetWaitStatus(node, ws, 0);

        //获取head节点的下一节点
        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);
    }

之前被阻塞的线程B会继续执行

    private final boolean parkAndCheckInterrupt() {
        LockSupport.park(this);
        return Thread.interrupted();
    }

 会继续执行

    final boolean acquireQueued(final Node node, int arg) {
        boolean failed = true;
        try {
            boolean interrupted = false;
            for (;;) {
                //获取当前节点的前置节点
                final Node p = node.predecessor();
                //如果前置节点等于head,尝试再次获取
                if (p == head && tryAcquire(arg)) {
                    setHead(node);
                    p.next = null; // help GC
                    failed = false;
                    return interrupted;
                }
                //如果获取失败,执行shouldParkAfterFailedAcquire方法
                if (shouldParkAfterFailedAcquire(p, node) &&
                    parkAndCheckInterrupt())
                    interrupted = true;
            }
        } finally {
            if (failed)
                cancelAcquire(node);
        }
    }

 这次执行tryAcquire会成功,之后将node节点设置为head节点。

 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 next = node.next;
                if (next != null && next.waitStatus <= 0)
                    compareAndSetNext(pred, predNext, next);
            } else {
                unparkSuccessor(node);
            }

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

Condition

Condition是ReentrantLock中的一种条件控制对象,类似于Object中的wait和notify,但是比wait/notify更加方便。一个Object对象只能有一个控制条件,就是wait/notify。而ReentrantLock可以创建多个Condition条件等待,满足多条件等待与唤醒。

先写一个例子熟悉一下Condition的使用,

public class MoreThreadExecute {

    private static ReentrantLock lock = new ReentrantLock();
    private static Condition a = lock.newCondition();
    private static Condition b = lock.newCondition();
    private static Condition c = lock.newCondition();
    private static AtomicInteger t = new AtomicInteger(0);
    private static final int THREADS = 3;

    public static void main(String[] args) {

        MoreThreadExecute lockTest = new MoreThreadExecute();

        ExecutorService pool = Executors.newFixedThreadPool(THREADS);
        pool.execute(lockTest.new PrintTask("B thread",b, c, "B", 1));
        pool.execute(lockTest.new PrintTask("C thread",c, a, "C", 2));
        pool.execute(lockTest.new PrintTask("A thread",a, b, "A", 0));

    }

    class PrintTask implements Runnable {
        //本线程的条件
        private Condition me;
        //下个线程的条件
        private Condition next;
        private String message;
        private int index;
        private String name;

        public PrintTask(String name,Condition me, Condition next, String message, int index) {
            this.name = name;
            this.me = me;
            this.next = next;
            this.message = message;
            this.index = index;
        }

        @Override
        public void run() {
            while(true) {
                if (t.get() >6){
                    break;
                }
                try {
                    lock.lock();
                    while(t.get() % THREADS != index) {
                        try {
                            System.out.println(name+"invoked, but not me");
                            //不到本线程的时机,则等待
                            me.await();
                        } catch (InterruptedException e) {
                            if (Thread.interrupted()) {
                                e.printStackTrace();
                            }
                        }
                    }
                    System.out.println(message);
                    t.incrementAndGet();
                    //通知其他线程重新获取锁,该他们执行了
                    next.signalAll();
                }finally {
                    lock.unlock();
                }
            }
        }

    }
}

执行的就是打印A、B、C顺序执行。

下面开始Condition源码解析:

首先我们需要先创建ReentrantLock对象,接着调用lock.newCondition()方法创建Condition。

    public Condition newCondition() {
        return sync.newCondition();
    }
    
    final ConditionObject newCondition() {
        return new ConditionObject();
    }

ConditionObject类是AQS里的一个内部类。

接着我们看lock.await()方法。

        public final void await() throws InterruptedException {
            if (Thread.interrupted())
                throw new InterruptedException();
            //创建条件队列
            Node node = addConditionWaiter();
            //释放全部资源
            int savedState = fullyRelease(node);
            int interruptMode = 0;
            while (!isOnSyncQueue(node)) {
                LockSupport.park(this);
                if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
                    break;
            }
            if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
                interruptMode = REINTERRUPT;
            if (node.nextWaiter != null) // clean up if cancelled
                unlinkCancelledWaiters();
            if (interruptMode != 0)
                reportInterruptAfterWait(interruptMode);
        }

1、会先通过addConditionWaiter方法创建条件等待队列

        private Node addConditionWaiter() {
            Node t = lastWaiter;
            // If lastWaiter is cancelled, clean out.
            if (t != null && t.waitStatus != Node.CONDITION) {
                unlinkCancelledWaiters();
                t = lastWaiter;
            }
            Node node = new Node(Thread.currentThread(), Node.CONDITION);
            if (t == null)
                firstWaiter = node;
            else
                t.nextWaiter = node;
            lastWaiter = node;
            return node;
        }

创建一个Node节点,waitState为-2(条件等待)

2、调用fullyRelease方法,释放占用的资源。

    final int fullyRelease(Node node) {
        boolean failed = true;
        try {
            int savedState = getState();
            if (release(savedState)) {
                failed = false;
                return savedState;
            } else {
                throw new IllegalMonitorStateException();
            }
        } finally {
            if (failed)
                node.waitStatus = Node.CANCELLED;
        }
    }

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

这里为什么要释放掉全部资源?

因为我们调用Object.wait或者notify方法的前提一定是通过synchronized获取到了锁才可以。这里如果我们要await等待其他条件触发执行,那么我们需要把当前获得的锁全部释放才可以,如果失败,则会报IllegalMonitorStateException异常。

3、通过isOnSyncQueue方法判断是否在同步队列中。

   final boolean isOnSyncQueue(Node node) {
        if (node.waitStatus == Node.CONDITION || node.prev == null)
            return false;
        if (node.next != null) // If has successor, it must be on queue
            return true;
        /*
         * node.prev can be non-null, but not yet on queue because
         * the CAS to place it on queue can fail. So we have to
         * traverse from tail to make sure it actually made it.  It
         * will always be near the tail in calls to this method, and
         * unless the CAS failed (which is unlikely), it will be
         * there, so we hardly ever traverse much.
         */
        return findNodeFromTail(node);
    }

AQS中的Node分为两种队列,一种是需要获取锁的同步队列,另一个是Condition创建的条件队列。一个Node只能在一种队列中,如果在条件队列中,就不会在同步队列中。第一次调用await方法后,肯定不会在同步队列中,所以返回false。之后调用LockSupport.park(this);方法阻塞等待唤醒。

4、假设这时另一线程调用signalAll方法进行唤醒。

        public final void signalAll() {
            if (!isHeldExclusively())
                throw new IllegalMonitorStateException();
            Node first = firstWaiter;
            if (first != null)
                doSignalAll(first);
        }
        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();
        }

首先会判断当前线程是否获取到锁,如果没有会抛出IllegalMonitorStateException异常。接着会调用doSignalAll方法。

    private void doSignalAll(Node first) {
        lastWaiter = firstWaiter = null;
        do {
         Node next = first.nextWaiter;
         first.nextWaiter = null;
         transferForSignal(first);
         first = next;
       } while (first != null);
    }

    final boolean transferForSignal(Node node) {
        /*
         * If cannot change waitStatus, the node has been cancelled.
         */
        if (!compareAndSetWaitStatus(node, Node.CONDITION, 0))
            return false;

        /*
         * Splice onto queue and try to set waitStatus of predecessor to
         * indicate that thread is (probably) waiting. If cancelled or
         * attempt to set waitStatus fails, wake up to resync (in which
         * case the waitStatus can be transiently and harmlessly wrong).
         */
        Node p = enq(node);
        int ws = p.waitStatus;
        if (ws > 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL))
            LockSupport.unpark(node.thread);
        return true;
    }

方法会获取新创建的Condition的Node节点,执行transferForSignal方法,将Node节点的状态由-2(等待条件)置为0,并将该Node节点加入到同步队列中。并释放前一节点的线程。继续回到之前阻塞的地方

        public final void await() throws InterruptedException {
            if (Thread.interrupted())
                throw new InterruptedException();
            //创建条件队列
            Node node = addConditionWaiter();
            //释放全部资源
            int savedState = fullyRelease(node);
            int interruptMode = 0;
            while (!isOnSyncQueue(node)) {
                LockSupport.park(this);
                if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
                    break;
            }
            if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
                interruptMode = REINTERRUPT;
            if (node.nextWaiter != null) // clean up if cancelled
                unlinkCancelledWaiters();
            if (interruptMode != 0)
                reportInterruptAfterWait(interruptMode);
        }

 会再次进入isOnSyncQueue方法,判断当前节点是否进入同步队列。

    final boolean isOnSyncQueue(Node node) {
        if (node.waitStatus == Node.CONDITION || node.prev == null)
            return false;
        if (node.next != null) // If has successor, it must be on queue
            return true;
        return findNodeFromTail(node);
    }

    private boolean findNodeFromTail(Node node) {
        Node t = tail;
        for (;;) {
            if (t == node)
                return true;
            if (t == null)
                return false;
            t = t.prev;
        }
    }

由之前的signalAll方法,已将队列waitState由-2变为-1,并将node由等待队列加入到同步队列。这里可以从findNodeFromTail方法找到同步队列中包含当前Node,所以返回true。不满足while循环条件。

6、进入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())
                    interrupted = true;
            }
        } finally {
            if (failed)
                cancelAcquire(node);
        }
    }

尝试获取方法和lock加锁等待方法一样。node节点尝试获取。如果未获取到锁,则继续阻塞等待释放。如果获取了则继续向下执行。

7、接着会执行if (node.nextWaiter != null)判断。

        if (node.nextWaiter != null) // clean up if cancelled
            unlinkCancelledWaiters();
       
        private void unlinkCancelledWaiters() {
            Node t = firstWaiter;
            Node trail = null;
            while (t != null) {
                Node next = t.nextWaiter;
                if (t.waitStatus != Node.CONDITION) {
                    t.nextWaiter = null;
                    if (trail == null)
                        firstWaiter = next;
                    else
                        trail.nextWaiter = next;
                    if (next == null)
                        lastWaiter = trail;
                }
                else
                    trail = t;
                t = next;
            }
        }

unlinkCancelledWaiters方法清除条件队列上节点状态不为 CONDITION 的节点。

unlinkCancelledWaiters方法其实没有什么用,因为如果await超时或者线程中断时,会尝试将该node节点的waitState状态置为0,并加入到同步队列。

        while (!isOnSyncQueue(node)) {
            LockSupport.park(this);
            //假设设立被其他线程调用interrupt阻断或者被唤醒,进入checkInterruptWhileWaiting方法
            if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
                break;
        }

        private int checkInterruptWhileWaiting(Node node) {
            return Thread.interrupted() ?
                    (transferAfterCancelledWait(node) ? THROW_IE : REINTERRUPT) :
                    0;
        }
        //被阻断进入transferAfterCancelledWait
        final boolean transferAfterCancelledWait(Node node) {
            //会将该node节点置为0
            if (compareAndSetWaitStatus(node, Node.CONDITION, 0)) {
                enq(node);
                return true;
            }
            /*
             * If we lost out to a signal(), then we can't proceed
             * until it finishes its enq().  Cancelling during an
             * incomplete transfer is both rare and transient, so just
             * spin.
             */
            while (!isOnSyncQueue(node))
                Thread.yield();
            return false;
        }

即使这里不调用unlinkCancelledWaiters方法,在执行signalAll唤醒方法时,也不会将这些节点加入同步队列中。只是为了清理一下等待队列上节点状态不为 CONDITION 的节点。

    private void doSignalAll(Node first) {
        lastWaiter = firstWaiter = null;
        do {
         Node next = first.nextWaiter;
         first.nextWaiter = null;
         transferForSignal(first);
         first = next;
       } while (first != null);
    }

    final boolean transferForSignal(Node node) {
        /*
         * If cannot change waitStatus, the node has been cancelled.
         */
        if (!compareAndSetWaitStatus(node, Node.CONDITION, 0))
            return false;

        /*
         * Splice onto queue and try to set waitStatus of predecessor to
         * indicate that thread is (probably) waiting. If cancelled or
         * attempt to set waitStatus fails, wake up to resync (in which
         * case the waitStatus can be transiently and harmlessly wrong).
         */
        Node p = enq(node);
        int ws = p.waitStatus;
        if (ws > 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL))
            LockSupport.unpark(node.thread);
        return true;
    }

总结:

Condition 自己也维护了一个队列,该队列的作用是维护一个等待 signal 信号的队列,两个队列的作用是不同,事实上,每个线程也仅仅会同时存在以上两个队列中的一个。将设有线程 1、2,下面来具体过程。

线程1:

  • 线程1 调用 reentrantLock.lock时,持有锁。

  • 线程1 调用 await 方法,进入[条件等待队列],同时释放锁。

  • 线程1 获取到线程2 signal 信号,从 [条件等待队列] 进入 [同步等待队列]。

线程2:

  • 线程2 调用 reentrantLock.lock时,由于锁被线程1 持有,进入 [同步等待队列]。

  • 由于线程1 释放锁,线程2 从 [同步等待队列] 移除,获取到锁。线程2 调用 signal 方法,导致线程 1 被唤醒。

  • 线程2 调用 reentrantLock.unlock 。线程1 获取锁,继续循环。


条件等待队列

条件等待队列,指的是 Condition 内部自己维护的一个队列,不同于 AQS 的[同步等待队列]。它具有以下特点:

  • 要加入[条件等待队列]的节点,不能在 [同步等待队列]。
  • 从 [条件等待队列] 移除的节点,会进入[同步等待队列]。
  • 一个锁对象只能有一个[同步等待队列],但可以有多个[条件等待队列]。

猜你喜欢

转载自blog.csdn.net/yytree123/article/details/108856237