介绍
ReentrantLock是Java中的一种可重入锁,也称为重入锁。是一种递归无阻塞的同步机制。它可以等同于 synchronized 的使用,但是 ReentrantLock 提供了比 synchronized 更强大、灵活的锁机制,可以减少死锁发生的概率。
ReentrantLock实现了Lock接口,它提供了以下功能:
1.可重入特性:即同一个线程可以多次获得同一把锁,而不会造成死锁。
2.公平锁和非公平锁:ReentrantLock支持公平锁和非公平锁两种模式,默认情况下是非公平锁。
3.可中断特性:支持可中断的获取锁的方法,即在等待获取锁的过程中,允许被打断等待。
4.多条件变量:ReentrantLock提供了多个条件变量,可以在一个锁上创建多个等待队列。
相对于synchronized关键字,ReentrantLock的主要优点是更加灵活和可扩展。同时,相对于synchronized关键字,ReentrantLock的性能稍微慢一些,因为它需要显式地获取和释放锁。
public class ReentrantLock implements Lock, java.io.Serializable

常量&变量
    //序列化版本号
    private static final long serialVersionUID = 7373984872572414699L;
    /** Synchronizer providing all implementation mechanics */
    //同步器
    private final Sync sync;
构造方法
- ReentrantLock() 这是ReentrantLock的无参构造方法,它会创建一个非公平锁(fairness参数默认为false),即当多个线程争抢同一个锁时,先获取锁的线程不一定是先发起请求的线程。
- ReentrantLock(boolean fairness) 这是ReentrantLock的带参构造方法,fair参数指定了锁的公平性,如果fair为true,则表示该锁是公平锁,即多个线程争抢同一个锁时,先发起请求的线程会优先获取锁。如果fair为false,则表示该锁是非公平锁,即多个线程争抢同一个锁时,先获取锁的线程不一定是先发起请求的线程。
    /**
     * 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();
    }
内部类
ReentrantLock的内部类Sync是通过继承AQS(AbstractQueuedSynchronizer)实现的可重入锁。它维护了锁的状态和同步队列。
Sync中最重要的是tryAcquire(int acquires)方法和tryRelease(int releases)方法,它们实现了获取锁和释放锁的逻辑。在这两个方法中,会判断当前线程是否已经持有锁,如果已经持有则直接返回true,否则会将当前线程加入同步队列中,阻塞等待获取锁。
此外,Sync还重写了AQS中的其他方法,如tryAcquireShared(int acquires)和tryReleaseShared(int releases),以支持共享锁的获取和释放。
总之,Sync作为ReentrantLock的核心部分,为ReentrantLock提供了可重入锁机制的实现,保证了多线程环境下的线程同步和数据一致性。
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() {
            // 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
        }
    }
公平锁FairSync
FairSync是ReentrantLock的内部类,它继承了AQS(AbstractQueuedSynchronizer)类,并重写了tryAcquire方法和tryRelease方法。试图获取锁的线程会按照先进先出的顺序进行排队,并在锁可用时按照队列的顺序获取锁。因此,FairSync实现了公平锁的机制。
需要注意的是,由于FairSync实现了公平锁的机制,因此在高并发环境下性能可能会有所下降。因此,在实际开发中,需要根据具体情况来选择使用公平锁还是非公平锁。
    /**
     * Sync object for fair locks
     */
    static final class FairSync extends Sync {
        private static final long serialVersionUID = -3000897897090466540L;
        final void lock() {
            acquire(1);
        }
        /**
         * Fair version of tryAcquire.  Don't grant access unless
         * recursive call or no waiters or is first.
         */
        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;
        }
    }
公平锁中,直接调用 acquire 方法。
非公平锁NonfairSync
NonfairSync继承自Sync类,Sync类是ReentrantLock的同步基础类。NonfairSync的实现方式是,当一个线程释放锁时,它会检查是否有等待线程,如果有,则唤醒其中的一个线程。如果有多个等待线程,则唤醒其中一个线程的选择是任意的,没有任何保证。这种实现方式被称为“非公平锁”,因为等待时间比较长的线程可能会一直处于等待状态,直到某个线程释放锁时才被唤醒。
总的来说,NonfairSync的实现方式比较简单,而且效率也比较高。如果不需要对线程的执行顺序进行特殊控制,可以使用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);
        }
    }
非公平锁中,首先尝试将 AQS 中的 state 属性从 0 变成 1,如果成功,则代表获取锁资源成功;否则再调用 acquire 方法。
常用方法
ReentrantLock的实现借用了AQS,AQS中包含一个队列,当一个线程调用ReentrantLock的lock方法时,若此锁已经被别的线程获取,那么当前线程就会被封装为AQS中队列的节点,加入等待队列,调用park挂起,直到别的线程调用unlock解锁(内部是unpark),当前线程会被唤醒然后获取锁。
相关AbstractQueuedSynchronizer的方法,请参考AbstractQueuedSynchronizer源码
lock
lock内部调用了内部抽象类Sync(继承了AQS,AbstractQueuedSynchronizer)的实例sync的lock方法,此方法有两个实现:公平锁、非公平锁。对应两个实现了Sync的内部类:FairSync和NonfairSync
在公平锁的实现中,获取锁的lock方法实现为:
        final void lock() {
            //获取锁
            acquire(1); 
        }
非公平锁中,lock的实现为:
        final void lock() {
            // 尝试获取锁,也就是尝试把AQS中的state从0改为1
            if (compareAndSetState(0, 1)) 
                // 来到这里时,说明修改state成功,即获取锁资源成功
                // 将当前线程设置到AOS的exclusiveOwnerThread属性中,代表该线程拿到了锁资源(跟可重入锁有关)
                setExclusiveOwnerThread(Thread.currentThread()); 
            else
                // 再去获取锁
                acquire(1); 
        }
ReentrantLock的lock方法用于获得锁。它具有以下特点:
- 如果锁当前没有被其他线程持有,该方法会立即获得锁,并返回。
- 如果锁当前被其他线程持有,则当前线程会被阻塞,直到该锁被释放。
- 当前线程获得锁后,它可以重复调用lock方法多次,每调用一次就会增加锁的计数器。只有在锁的计数器为0时,锁才会真正被释放。
- 当前线程获得锁后,如果没有显式地释放锁,那么锁会一直被当前线程持有,直到当前线程退出或抛出异常,此时锁才会被自动释放。
- 如果当前线程在获得锁之前被中断,那么该方法会抛出InterruptedException异常。
- lock方法是可重入的,即同一个线程可以重复获得已经持有的锁,不会造成死锁。
acquire
AbstractQueuedSynchronizer.java
    /**
     * 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) &&
            //addWaiter:将线程封装到 Node 节点并添加到队列尾部
            //acquireQueued查看当前排队的 Node 是否在队列的前面,如果在前面,尝试获取锁资源。如果没在前面,线程进入到阻塞状态。
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
            selfInterrupt();
    }
在公平锁中,tryAcquire方法会先查看state是否为0,再看是否有线程排队,若有就直接去排队。非公平锁的tryAcquire会先查看state是否为0,若为0就尝试修改state来获取锁。如果state为1,还会额外检查一下此次获取锁是否为可重入操作,若是,就直接获取锁。
addWaiter会在线程没有拿到锁资源时(tryAcquire返回true时),将当前线程封装为Node对象,把这个Node对象加入AQS的等待队列中去。
acquireQueued会检查当前线程是否排在队伍前面,若是就尝试获取锁,若长期未获得锁,就挂起当前线程。
tryAcquire
tryAcquire() 方法分公平锁和非公平锁两套实现,主要做了两件事:
如果 AQS 当前 state 为 0,尝试获取锁资源。
如果 AQS 当前 state 不为 0,查看是否是可重入操作。
公平锁
        /**
         * Fair version of tryAcquire.  Don't grant access unless
         * recursive call or no waiters or is first.
         */
        protected final boolean tryAcquire(int acquires) {
            //获取当前线程
            final Thread current = Thread.currentThread();
            //获取AQS当前state值
            int c = getState();
            // 判断state是否为0,为0则代表当前没有线程持有锁
            if (c == 0) {
                // 首先判断是否有线程在排队,如果有,tryAcquie()方法直接返回false
                // 如果没有,则尝试获取锁资源
                if (!hasQueuedPredecessors() &&
                    //将state设置为acquires
                    compareAndSetState(0, acquires)) {
                    //将当前线程设置为锁持有者线程
                    setExclusiveOwnerThread(current);
                    return true;
                }
            }
            // 如果state != 0,则代表有线程持有锁资源
            // 判断占有锁的线程是不是当前线程,如果是,则进行可重入操作
            else if (current == getExclusiveOwnerThread()) {
                // 可重入state的预期值
                int nextc = c + acquires;
                // 检查锁重入是否超过最大值,二进制第一位表示符号
                //01111111 11111111 11111111 11111111  超过最大值为负值
                if (nextc < 0)
                    throw new Error("Maximum lock count exceeded");
                setState(nextc);
                return true;
            }
            return false;
        }
    }
非公平锁
        /**
         * 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();
            //aqs的state状态值
            int c = getState();
            //如果state == 0,说明没有线程占用着当前的锁资源
            if (c == 0) {
                // CAS直接尝试获取锁资源,直接抢锁,不管有没有线程在队列中
                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;
        }
公平锁比非公平锁多了一步操作,就是当锁空闲时,查看一下是否有线程在排队,再决定是否尝试获取锁;非公平锁则不管是否有线程在排队,直接去抢锁。
addWaiter
AbstractQueuedSynchronizer.java
没有拿到锁资源时,当前线程就要被封装为Node对象并排队,会调用addWaiter方法。此方法在AQS中实现,因为要对AQS中的队列(双向链表)进行操作。
unlock
释放锁,将state从正整数更改为0,并且会将AQS中队列中挂起的线程唤醒(会有unpark方法)允许其他线程获得这个锁。
    /**
     * Attempts to release this lock.
     *
     * <p>If the current thread is the holder of this lock then the hold
     * count is decremented.  If the hold count is now zero then the lock
     * is released.  If the current thread is not the holder of this
     * lock then {@link IllegalMonitorStateException} is thrown.
     *
     * @throws IllegalMonitorStateException if the current thread does not
     *         hold this lock
     */
    public void unlock() {
        sync.release(1);
    }
在内部,它调用了同步对象sync的release方法,该方法会释放一个锁定计数器,允许其他线程获得锁。
注意:调用unlock方法的线程必须持有锁,否则会抛出IllegalMonitorStateException异常。
release
AbstractQueuedSynchronizer.java
释放锁 release方法(位于AQS)
    /**
     * 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}
     * release利用tryRelease先进行释放锁,tryRealse是由子类实现的方法,可以确保线程是获取到锁的,并进行释放锁,
     * unparkSuccessor主要是利用LockSupport.unpark唤醒线程;
     */
    public final boolean release(int arg) {
        //尝试释放锁,这个方法是由子类实现的方法
        if (tryRelease(arg)) {
            Node h = head;
            //头节点不为如果节点状态不是CANCELLED,也就是线程没有被取消,也就是不为0的,就进行唤醒
            if (h != null && h.waitStatus != 0)
                //唤醒线程
                unparkSuccessor(h);
            return true;
        }
        return false;
    }
tryRelease
tryRelease在AQS中只抛出了一个异常,Sync对此方法进行了重写
该方法的作用是将锁的状态值减去传入的参数releases,并检查当前线程是否持有锁。如果当前线程不是持有锁的线程,则抛出IllegalMonitorStateException异常。如果锁的状态值为0,则表示该锁已经被完全释放,此时将锁的拥有者设置为null,并将锁的状态值设置为c,即0。如果锁的状态值不为0,则将锁的状态值设置为c。最后,如果锁已经完全释放,则返回true,否则返回false。
需要注意的是,在使用ReentrantLock时,应该使用try-finally块来确保锁的释放,避免出现死锁等问题。
        //这个方法主要是确保了当前线程持有的锁,不是抛出异常,确保线程一定获取到锁的线程,并进行响应的释放锁:
        protected final boolean tryRelease(int releases) {
            //将线程的state计时器减-1,state为0代表没有线程持有锁,大于0则代表有线程持有锁了
            int c = getState() - releases;
            // 如果释放锁的线程不是占用锁的线程,抛异常
            if (Thread.currentThread() != getExclusiveOwnerThread())
                throw new IllegalMonitorStateException();
            // 是否成功的将锁资源释放
            boolean free = false;
            //没有线程持有锁
            if (c == 0) {
                // 如果state = 0,代表成功释放锁资源
                free = true;
                //将记录持有线程的变量置为空
                setExclusiveOwnerThread(null);
            }
            setState(c);
            return free;
        }



















