JDK1.8源码解析之ReentrantLock

244 阅读12分钟

前言

  • 可重入的互斥{@link Lock},具有与使用{@code sync}方法和语句访问的隐式监视器锁相同的基本行为和语义,但具有扩展的功能。

  • 上一次成功锁定但尚未解锁的线程拥有{@code ReentrantLock}。

  • 当该锁不属于另一个线程时,调用{@code lock}的线程将返回,并成功获取该锁。

  • 如果当前线程已经拥有该锁,则该方法将立即返回。

  • 可以使用方法{@link #isHeldByCurrentThread}和{@link #getHoldCount}进行检查。

  • 此类的构造函数接受一个可选的fairness参数。

  • 设置{@code true}时,在争用下,锁倾向于授予对等待时间最长的线程的访问。

  • 否则,此锁不能保证任何特定的访问顺序。

  • 使用许多线程访问的公平锁定的程序可能会比使用默认设置的程序显示较低的总体吞吐量(即较慢;通常要慢得多),但获得锁定并保证没有饥饿的时间差异较小。

  • 但是请注意,锁的公平性不能保证线程调度的公平性。

  • 因此,使用公平锁的多个线程之一可能会连续多次获得它,而其他活动线程没有进行且当前未持有该锁。

  • 另请注意,未计时的{@link #tryLock()}方法不支持公平性设置。

  • 如果锁定可用,即使其他线程正在等待,它将成功。

  • 建议的做法是始终立即使用{@code try}块对{@code lock}进行调用,通常是在构造之前/之后,例如: class X { private final ReentrantLock lock = new ReentrantLock(); // ... public void m() { lock.lock(); // block until condition holds try { // ... method body } finally { lock.unlock() }

  • 除了实现{@link Lock}接口之外,此类还定义了许多{@code public}和{@code protected}方法来检查锁的状态。

  • 其中一些方法仅对仪器和监视有用。

  • 此类的序列化与内置锁的行为相同:反序列化的锁处于解锁状态,而不管序列化时的状态如何。

  • nm该锁通过同一线程最多支持2147483647递归锁。

  • 尝试超过此限制会导致锁定方法引发{@link Error}。

源码

package java.util.concurrent.locks;

public class ReentrantLock implements Lock, java.io.Serializable {
    private static final long serialVersionUID = 7373984872572414699L;
    /** Synchronizer providing all implementation mechanics */
    private final Sync sync;

    /**
     * Base of synchronization control for this lock. Subclassed
     * into fair and nonfair versions below. Uses AQS state to
     * represent the number of holds on the lock.
     */
    abstract static class Sync extends AbstractQueuedSynchronizer {
        private static final long serialVersionUID = -5179523762034025860L;

        /**
         * Performs {@link Lock#lock}. The main reason for subclassing
         * is to allow fast path for nonfair version.
         */
        abstract void lock();

        /**
         * 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);
                return true;
            }
            return 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;
        }

        protected final boolean isHeldExclusively() {
            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
        }
    }

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

    /**
     * Sync object for fair locks
     */
    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)) {
                    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;
        }
    }

    /**
     * 创建{@code ReentrantLock}的实例。这等效于使用{@code ReentrantLock(false)}。
     */
    public ReentrantLock() {
        sync = new NonfairSync();
    }

    /**
     * 使用给定的公平性策略创建{@code ReentrantLock}的实例。
     *
     * @param fair {@code true} if this lock should use a fair ordering policy
     */
    public ReentrantLock(boolean fair) {
        sync = fair ? new FairSync() : new NonfairSync();
    }

    /**
     * 获取锁。
     * 如果没有其他线程持有该锁,则获取该锁并立即返回,将锁保持计数设置为1。
     * 如果当前线程已经持有该锁,则持有计数将增加一,该方法将立即返回。
     * 如果锁是由另一个线程持有的,则当前线程将出于线程调度目的而被禁用,并处于休眠状态,直到获取了该锁为止,此时,锁持有计数被设置为1。
     */
    public void lock() {
        sync.lock();
    }

    /**
     * 除非当前线程为{@linkplain Thread#interrupt interrupted},否则获取锁。
     * 如果没有其他线程持有该锁,则获取该锁并立即返回,将锁保持计数设置为1。
     * 如果当前线程已经持有此锁,则持有计数将增加一,并且该方法将立即返回。
     * 如果锁是由另一个线程持有的,则出于线程调度目的,当前线程将被禁用,并处于休眠状态,直到发生以下两种情况之一为止:该锁由当前线程获取;或某个其他线程{@linkplain Thread#interrupt interrupts}当前线程。
     * 如果当前线程获取了锁,则锁保持计数将设置为1。
     * 如果当前线程:在进入此方法时设置了其中断状态;或在获取锁定时被{@linkplain Thread#interrupt interrupted中断},则抛出{@link InterruptedException}并清除当前线程的中断状态。
     * 在此实现中,由于此方法是显式的中断点,因此优先于对中断的响应而不是正常或可重入的锁获取。
     */
    public void lockInterruptibly() throws InterruptedException {
        sync.acquireInterruptibly(1);
    }

    /**
     * 仅当调用时另一个线程未持有该锁时才获取该锁。
     * 如果没有其他线程持有该锁,则获取该锁,并立即返回值{@code true},并将锁保持计数设置为1。
     * 即使已将此锁设置为使用公平的排序策略,无论是否有其他线程当前正在等待该锁,对{@code tryLock()}的调用都将立即获取该锁。
     * 即使破坏公平性,这种“讨价还价”的行为在某些情况下还是有用的。
     * 如果要遵守此锁的公平性设置,请使用几乎等效的{@link #tryLock(long,TimeUnit)tryLock(0,TimeUnit.SECONDS)}(它还会检测到中断)。
     * 如果当前线程已经持有此锁,则持有计数将增加一,并且该方法返回{@code true}。
     * 如果锁由另一个线程持有,则此方法将立即返回值{@code false}。
     */
    public boolean tryLock() {
        return sync.nonfairTryAcquire(1);
    }

    /**
     *
     * 如果在给定的等待时间内另一个线程未持有该锁,并且当前线程尚未{@linkplain Thread#interrupt interrupted},则获取该锁。
     * 如果没有其他线程持有该锁,则获取该锁,并立即返回值{@code true},并将锁保持计数设置为1。
     * 如果将此锁设置为使用公平的排序策略,那么如果有任何其他线程在等待该锁,则不会获取可用锁。
     * 这与{@link #tryLock()}方法相反。
     * 如果您想要一个定时的{@code tryLock}确实允许在公平锁上插入,则将定时和非定时表单组合在一起:
     if (lock.tryLock() ||
          lock.tryLock(timeout, unit)) {
        ...
     }}
     *
     * 如果当前线程已经持有此锁,则持有计数将增加一,并且该方法返回{@code true}。
     * 如果该锁由另一个线程持有,则出于线程调度目的,当前线程将被禁用,并处于休眠状态,直到发生以下三种情况之一为止:该锁被当前线程获取;或其他某个线程{@linkplain Thread#interrupt interrupts}当前线程;或经过指定的等待时间。
     * 如果获取了锁,则返回值{@code true},并将锁保持计数设置为1。
     * 如果当前线程:在进入此方法时设置了其中断状态;或在获取锁定时被{@linkplain Thread#interrupt interrupted中断},则抛出{@link InterruptedException}并清除当前线程的中断状态。
     * 如果经过了指定的等待时间,则返回值{@code false}。
     * 如果时间小于或等于零,则该方法将根本不等待。
     * 在此实现中,由于此方法是显式的中断点,因此优先于对中断的响应而不是正常或可重入的锁定获取,而是优先报告等待时间的流逝。
     */
    public boolean tryLock(long timeout, TimeUnit unit)
            throws InterruptedException {
        return sync.tryAcquireNanos(1, unit.toNanos(timeout));
    }

    /**
     * 尝试释放此锁。
     * 如果当前线程是此锁的持有者,则保留计数将减少。
     * 如果保持计数现在为零,则释放锁定。
     * 如果当前线程不是此锁的持有者,则将引发{@link IllegalMonitorStateException}。
     */
    public void unlock() {
        sync.release(1);
    }

    /**
     * 返回与此{@link Lock}实例一起使用的{@link Condition}实例。
     * 返回的{@link Condition}实例与{@link Object}监视方法({@link Object#wait()wait},{@ link Object#notify notify}和{@link Object#notifyAll当与内置监视器锁定一起使用时)。
     * 如果在调用任何{@link Condition} {@linkplain Condition#await()等待}或{@linkplain Condition#signal信号通知}方法时未保持此锁定,则将引发{@link IllegalMonitorStateException}。
     * 当调用条件{@linkplain Condition#await()等待方法}方法时,将释放该锁,并在它们返回之前,重新获取该锁,并将锁保持计数恢复为调用该方法时的状态。
     * 如果线程在等待时被{@linkplain Thread#interrupt interrupted中断},则等待将终止,将引发{@link InterruptedException}并清除线程的中断状态。
     * 等待线程以FIFO顺序发出信号。
     * 从等待方法返回的线程的锁重新获取顺序与最初获取锁的线程的顺序相同(默认情况下未指定),但对于公平锁,优先使用等待时间最长的线程。
     */
    public Condition newCondition() {
        return sync.newCondition();
    }

    /**
     * 查询当前线程对该锁的保持次数。
     * 对于每个未与解锁动作匹配的锁定动作,线程都会拥有一个锁。
     * 保留计数信息通常仅用于测试和调试目的。
     * 例如,如果不应该使用已经持有的锁来输入特定的代码段,那么我们可以断言以下事实:
     *
     * class X {
     *   ReentrantLock lock = new ReentrantLock();
     *   // ...
     *   public void m() {
     *     assert lock.getHoldCount() == 0;
     *     lock.lock();
     *     try {
     *       // ... method body
     *     } finally {
     *       lock.unlock();
     *     }
     *   }
     * }}
     */
    public int getHoldCount() {
        return sync.getHoldCount();
    }

    /**
     * 查询此锁是否由当前线程持有。
     * 与内置监视器锁定的{@link Thread#holdsLock(Object)}方法类似,此方法通常用于调试和测试。
     * 例如,仅在持有锁的情况下才应调用的方法可以断言是这种情况:
     * class X {
     *   ReentrantLock lock = new ReentrantLock();
     *   // ...
     *
     *   public void m() {
     *       assert lock.isHeldByCurrentThread();
     *       // ... method body
     *   }
     * }}
     * 它还可以用于确保以非可重入方式使用可重入锁,例如:
     
     * class X {
     *   ReentrantLock lock = new ReentrantLock();
     *   // ...
     *
     *   public void m() {
     *       assert !lock.isHeldByCurrentThread();
     *       lock.lock();
     *       try {
     *           // ... method body
     *       } finally {
     *           lock.unlock();
     *       }
     *   }
     * }}
     */
    public boolean isHeldByCurrentThread() {
        return sync.isHeldExclusively();
    }

    /**
     * 查询此锁是否由任何线程持有。此方法设计用于监视系统状态,而不用于同步控制。
     */
    public boolean isLocked() {
        return sync.isLocked();
    }

    /**
     * Returns {@code true} if this lock has fairness set true.
     *
     * @return {@code true} if this lock has fairness set true
     */
    public final boolean isFair() {
        return sync instanceof FairSync;
    }

    /**
     * 返回当前拥有此锁的线程;如果不拥有,则返回{@code null}。
     * 当非所有者的线程调用此方法时,返回值反映当前锁定状态的尽力而为近似。
     * 例如,即使有线程试图获取锁,但所有者尚未拥有所有者,所有者可能会暂时{@code null}。
     * 设计此方法是为了便于构造提供更广泛的锁监视功能的子类。
     */
    protected Thread getOwner() {
        return sync.getOwner();
    }

    /**
     * 查询是否有任何线程正在等待获取此锁。
     * 请注意,由于取消可能随时发生,因此{@code true}返回值不能保证任何其他线程都将获得此锁。
     * 此方法主要设计用于监视系统状态。
     */
    public final boolean hasQueuedThreads() {
        return sync.hasQueuedThreads();
    }

    /**
     * 查询给定线程是否正在等待获取此锁。
     * 请注意,由于取消可能随时发生,因此返回{@code true}并不能保证该线程将获得此锁。
     * 此方法主要设计用于监视系统状态。
     */
    public final boolean hasQueuedThread(Thread thread) {
        return sync.isQueued(thread);
    }

    /**
     * 返回等待获取此锁的线程数的估计值。
     * 该值只是一个估计值,因为在此方法遍历内部数据结构时,线程数可能会动态变化。
     * 此方法设计用于监视系统状态,而不用于同步控制。
     */
    public final int getQueueLength() {
        return sync.getQueueLength();
    }

    /**
     * 返回一个包含可能正在等待获取此锁的线程的集合。
     * 因为实际的线程集在构造此结果时可能会动态变化,所以返回的集合只是尽力而为的估计。
     * 返回的集合的元素没有特定的顺序。
     * 设计此方法是为了便于构造子类,以提供更广泛的监视功能。
     *
     */
    protected Collection<Thread> getQueuedThreads() {
        return sync.getQueuedThreads();
    }

    /**
     * 查询是否有任何线程正在等待与此锁关联的给定条件。
     * 请注意,由于超时和中断可能随时发生,因此{@code true}返回并不保证将来的{@code signal}会唤醒任何线程。
     * 此方法主要设计用于监视系统状态。
     */
    public boolean hasWaiters(Condition condition) {
        if (condition == null)
            throw new NullPointerException();
        if (!(condition instanceof AbstractQueuedSynchronizer.ConditionObject))
            throw new IllegalArgumentException("not owner");
        return sync.hasWaiters((AbstractQueuedSynchronizer.ConditionObject)condition);
    }

    /**
     * 返回等待与此锁关联的给定条件的线程数的估计值。
     * 请注意,由于超时和中断可能随时发生,因此估算值仅用作实际侍者数的上限。
     * 此方法设计用于监视系统状态,而不用于同步控制。
     */
    public int getWaitQueueLength(Condition condition) {
        if (condition == null)
            throw new NullPointerException();
        if (!(condition instanceof AbstractQueuedSynchronizer.ConditionObject))
            throw new IllegalArgumentException("not owner");
        return sync.getWaitQueueLength((AbstractQueuedSynchronizer.ConditionObject)condition);
    }

    /**
     * 返回一个包含那些可能正在等待与此锁相关的给定条件的线程的集合。
     * 因为实际的线程集在构造此结果时可能会动态变化,所以返回的集合只是尽力而为的估计。
     * 返回的集合的元素没有特定的顺序。
     * 设计此方法是为了便于构造提供更广泛的状态监视工具的子类。
     */
    protected Collection<Thread> getWaitingThreads(Condition condition) {
        if (condition == null)
            throw new NullPointerException();
        if (!(condition instanceof AbstractQueuedSynchronizer.ConditionObject))
            throw new IllegalArgumentException("not owner");
        return sync.getWaitingThreads((AbstractQueuedSynchronizer.ConditionObject)condition);
    }

    /**
     * 返回标识此锁定及其锁定状态的字符串。
     * 括号中的状态包括拥有线程的字符串{@code“ Unlocked”}或字符串{@code“ Locked by”},
     * 后跟{@linkplain Thread#getName name}。
     */
    public String toString() {
        Thread o = sync.getOwner();
        return super.toString() + ((o == null) ?
                                   "[Unlocked]" :
                                   "[Locked by thread " + o.getName() + "]");
    }
}