In-depth analysis of the principle of ReentrantLock --- based on jdk1.8

In-depth analysis of the principle of ReentrantLock — based on jdk1.8

ReentrantLock mainly explains lock and unlock methods. Let's see how he realized the reentrancy of locking and waiting for the lock to be released.

1. First look at the initialization method

    /**
     * Creates an instance of {@code ReentrantLock}.
     * This is equivalent to using {@code ReentrantLock(false)}.
     */
    public ReentrantLock() {
        sync = new NonfairSync(); // 默认选择非公平锁
    }

    /**
     * Creates an instance of {@code ReentrantLock} with the
     * given fairness policy.
     *
     * @param fair {@code true} if this lock should use a fair ordering policy
     */
    public ReentrantLock(boolean fair) {
        sync = fair ?
        new FairSync()  // 非平锁
        :
        new NonfairSync(); // 公平锁
    }

There are two internal classes mentioned here
1. new FairSync ();
2. new NonfairSync ();
their main difference is the lock method
1. NonfairSync will first try to acquire the lock and then will be added to the queue and enter the waiting state after the acquisition fails.
2. FairSync first determine whether the lock is occupied by other threads, if so enters the tail of the queue waiting for
them to inherit the ReentrantLock.Sync class, ReentrantLock.Sync class also inherited the AbstractQueuedSynchronizer
1. ReentrantLock.Sync some public methods of class
2. internal AbstractQueuedSynchronizer maintains a A queue implemented by a doubly linked list is used to record threads waiting for lock release,

In actual operation, NonfairSync is used more. Here is a study of
NonfairSync for NonfairSync:

  /**
   * 非公平锁的同步对象
   * 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() {
          if (compareAndSetState(0, 1))
              setExclusiveOwnerThread(Thread.currentThread());
          else
              acquire(1);
      }

      protected final boolean tryAcquire(int acquires) {
          return nonfairTryAcquire(acquires);
      }
  }
  1. First look at the lock method
        final void lock() {
            //  通过原子操作 改变上锁状态
            if (compareAndSetState(0, 1)) // 变更成功
                setExclusiveOwnerThread(Thread.currentThread()); // 设置持有者为当前线程
            else // 变更成功
                acquire(1);
        }

Down the card in code order
1.1 compareAndSetState method

    private static final Unsafe unsafe = Unsafe.getUnsafe();

    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); }
    }

    /**
     *  通过原子操作 改变上锁状态
     * @param expect the expected value
     * @param update the new value
     * @return {@code true} if successful. False return indicates that the actual
     *         value was not equal to the expected value.
     */
    protected final boolean compareAndSetState(int expect, int update) {
        // See below for intrinsics setup to support this  调用本地方法 实现硬件级别的原子操作 cas
        return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
    }

Unsafe:
1. The core class of CAS (CAS comparison and exchange)
2. The operation of memory through local methods to achieve cas
3. Do not operate this class in the case of a half- knowledge

1.2 setExclusiveOwnerThread (Thread.currentThread ()); nothing to say

1.3 AbstractQueuedSynchronizer.acquire();

    /**
     * Acquires in exclusive mode, ignoring interrupts.  Implemented
     * by invoking at least once {@link #tryAcquire},
     * returning on success.  Otherwise the thread is queued, possibly
     * repeatedly blocking and unblocking, invoking {@link
     * #tryAcquire} until success.  This method can be used
     * to implement method {@link Lock#lock}.
     *
     * @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) && // 再次尝试上锁 回到了  NonfairSync.tryAcquire 方法, tryAcquire 调用了 Sync.nonfairTryAcquire方法
            acquireQueued(
                    addWaiter(Node.EXCLUSIVE), // 链表尾部添加节点
                    arg
                )
            )
            selfInterrupt();
    }

1.3.1 nonfairTryAcquire:

    /**
         * 判断 reentranLock 状态 是否被锁住(state ?= 0)
         * <p>如果没被锁住尝试 原子性上锁 失败返回false</>
         * <p>如果被锁住 判断是否是当前线程持有锁(重入锁的实现) 如果是 state + 1
         * (信号量  记录该线程持有锁的次数。 该线程每次释放所 信号量 -1。 信号量为零 代表 锁被真正释放)</>
         * <p>else 返回false</p>
         * 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) { // 如果锁已被经释放 再次尝试获取锁
                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); // 累加 state 的值  此段代码 实现了重入锁
                return true;
            }
            return false;
        }

1.3.2 addWaiter code:

    /**
     *
     * 把当前线程加入队列 尾部
     *
     * 负责队列初始化
     * 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;
        if (pred != null) { // 列队尾部不为空
            node.prev = pred;
            if (compareAndSetTail(pred, node)) {
                pred.next = node;
                return node;
            }
        }
        // 列队尾部为空 或者  CAS 操作失败
        enq(node);
        return node;
    }

    /**
     * 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) { // 尾部不为空 不断尝试  CAS 操作
                if (compareAndSetHead(new Node()))
                    tail = head;
            } else { // 尾部为空 尝试构建表结构
                node.prev = t;
                if (compareAndSetTail(t, node)) {
                    t.next = node;
                    return t;
                }
            }
        }
    }

1.3.3 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);  // TODO: 2018/1/29  抛出异常 才会走的到这里。  源码在下面
        }
    }

    /**
     * 检查 是否需要阻塞当前线程
     * Checks and updates status for a node that failed to acquire.
     * Returns true if thread should block. This is the main signal
     * control in all acquire loops.  Requires that pred == node.prev.
     *
     * @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)
            /* 后继节点在等待唤醒  也就是当前节点需要进入等待状态了
             * 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.prev = pred = pred.prev;
            } while (pred.waitStatus > 0);
            pred.next = node;
        } else {
            /*  设置前驱节点为 SIGNAL 标记自己为等待唤醒 下次循环到这里之前 如果没有成功拥有锁, 则会进入   if (ws == Node.SIGNAL) 代码段
             * 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.
             */
            compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
        }
        return false;
    }

    private final boolean parkAndCheckInterrupt() {
        LockSupport.park(this); // 又是一个底层类 实现线程等待
        return Thread.interrupted(); // 返回并 取消等待状态
    }

    /**
     * Cancels an ongoing attempt to acquire.
     * 列队等待中 抛出异常会调用此方法
     * @param node the node
     */
    private void cancelAcquire(Node node) {
        // Ignore if node doesn't exist
        if (node == null)
            return;

        node.thread = null; // 释放线程

        // 前驱节点已被取消  重新定义前驱节点
        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; // 取消当前线程 所属的节点(标记为取消),  没有使用 cas  因为 其他线程 不会干扰这里

        // 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.
            // 如果node既不是tail,又不是head的后继节点
            // 则将node的前继节点的waitStatus置为SIGNAL
            // 并使node的前继节点指向node的后继节点(相当于将node从队列中删掉了)
            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 {
                //  如果node是head的后继节点,则直接唤醒node的后继节点
                unparkSuccessor(node);
            }

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

    /** 唤醒后继节点
     * 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) //置零当前线程所在的结点状态,允许失败。
            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);  // 唤醒下级节点
    }

The implementation of the lock method is probably the above

From the above, you can probably guess how unlock works. . . Then write here first. . . .
leftover. . . . Guess

Published 17 original articles · won 24 · views 280,000 +

Guess you like

Origin blog.csdn.net/qq_22956867/article/details/79257998