ReentrantLock源码分析

ReentrantLock源码分析

ReentrantLock是独享锁,同时是基于AQS实现的,因此它底层肯定是通过自定义AQS独占模式下的同步器来实现独享锁,该同步器需要重写AQS提供的tryAcquire()和tryRelease()方法,只需要告诉AQS是否尝试获取同步资源和释放同步资源成功即可。

AQS子类需要定义以及维护同步状态的值,在ReentrantLock中使用state为0表示锁没有被线程所持有,使用state不为0表示锁经被线程所持有。

ReentrantLock有公平和非公平两种模式,公平模式即多线程按照申请锁的顺序来获取锁,非公平模式即多线程并非按照申请锁的顺序来获取锁。


ReentrantLock的结构

public class ReentrantLock implements Lock, java.io.Serializable {
    
    private final Sync sync;

    /**
     * 抽象同步器(AQS独占模式)
     */
    abstract static class Sync extends AbstractQueuedSynchronizer {
       // ......
    }
    
    /**
     * 非公平同步器
     */
    static final class NonfairSync extends Sync {
       // ......
    }

    /**
     * 公平同步器
     */
    static final class FairSync extends Sync {
      // ......
    }

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

    public ReentrantLock(boolean fair) {
        sync = fair ? new FairSync() : new NonfairSync();
    }

    public void lock() {
        sync.lock();
    }

    public boolean tryLock() {
        return sync.nonfairTryAcquire(1);
    }

    public boolean tryLock(long timeout, TimeUnit unit)
            throws InterruptedException {
        return sync.tryAcquireNanos(1, unit.toNanos(timeout));
    }

    public void unlock() {
        sync.release(1);
    }
  
    // 其它省略
}

可以看到ReentrantLock中定义了一个抽象同步器(Sync)、非公平同步器(NonfairSync)、公平同步器(FairSync),同时非公平同步器和公平同步器都继承抽象同步器。

同时ReentrantLock中存在一个全局的抽象同步器属性,然后通过构建方法来进行初始化,通过fair参数来指定到底是使用公平同步器还是非公平同步器,默认情况下是使用非公平同步器。

同时ReentrantLock中的lock()方法将会调用抽象同步器声明的lock()方法,unlock()方法将会直接调用AQS的release()方法,而tryLock()方法将会调用抽象同步器提供的nonfairTryAcquire()方法。


剖析抽象同步器

/**
 * 抽象同步器(AQS独占模式)
 */ 
abstract static class Sync extends AbstractQueuedSynchronizer {
    private static final long serialVersionUID = -5179523762034025860L;

    /**
     * 声明了lock()方法,用于加锁
     * ReentrantLock的lock()方法会调用抽象同步器声明的lock()方法
     */ 
    abstract void lock();

    /**
     * 尝试获取锁,如果获取成功则返回true,否则返回false
     * ReentrantLock的tryLock()方法会调用抽象同步器提供的nonfairTryAcquire()方法
     */
    final boolean nonfairTryAcquire(int acquires) {
        final Thread current = Thread.currentThread();
        int c = getState();
        // 如果同步状态为0,表示锁没有被线程所持有
        if (c == 0) {
            // 则通过CAS将同步状态设置为1,表示获取锁
            if (compareAndSetState(0, acquires)) {
                setExclusiveOwnerThread(current); // 将拥有同步资源的线程设置为当前线程
                return true;
            }
        }
        // 锁已经被线程所持有,同时如果当前线程就是拥有锁的线程,它还尝试获取锁,那么就累加同步状态的值,这种情况也返回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;
    }

    /**
     * 重写AQS的tryRelease()方法,尝试释放锁,如果释放成功,则返回true,否则返回false
     */ 
    protected final boolean tryRelease(int releases) {
  		// 获取锁释放后剩余的同步资源
        int c = getState() - releases;
        // 如果当前线程并非拥有锁的线程,则直接抛出异常(要求谁加的锁只能由谁进行释放)
        if (Thread.currentThread() != getExclusiveOwnerThread())
            throw new IllegalMonitorStateException();
        
        boolean free = false;
        // 如果锁释放后,同步状态的值为0,则返回true,否则说明线程加了不止一把锁,然后它又没有释放全,这种情况会更新同步状态的值,然后返回false
        if (c == 0) {
            free = true;
            setExclusiveOwnerThread(null); // 清空拥有同步资源的线程
        }
        setState(c);
        return free;
    }

    /**
     * 判断当前线程是否持有锁
     */
    protected final boolean isHeldExclusively() {
        return getExclusiveOwnerThread() == Thread.currentThread();
    }

    /**
     * 返回Condition实例,在某些场景下可以进行阻塞与唤醒
     */
    final ConditionObject newCondition() {
        return new ConditionObject();
    }

	/**
	 * 获取拥有锁的线程
	 */
    final Thread getOwner() {
        return getState() == 0 ? null : getExclusiveOwnerThread();
    }

    /**
     * 返回当前线程拥有锁的个数
     */
    final int getHoldCount() {
        return isHeldExclusively() ? getState() : 0;
    }

    /**
     * 判断锁是否已经被线程所持有
     */
    final boolean isLocked() {
        return getState() != 0;
    }

}

抽象同步器声明了lock()方法,该方法需要由抽象同步器的子类来进行实现,ReentrantLock的lock()方法将会调用抽象同步器声明的lock()方法。

同时抽象同步器提供了nonfairTryAcquire()方法,如果同步状态的值为0,表示锁没有被线程所持有,则通过CAS尝试获取锁,如果获取锁成功则返回true,同时如果同步状态的值不为0,表示锁已经被线程所持有了,同时如果当前线程就是拥有锁的线程,它还进行加锁,那么就累加同步状态的值,这种情况也会返回true,否则返回false, 该方法会在ReentrantLock的tryLock()方法以及非公平同步器的tryAcquire()方法中被调用。

同时抽象同步器已经重写了AQS的tryRelease()方法,因此抽象同步器的子类还需要重写AQS的tryAcquire()方法。


剖析非公平同步器

/**
 * 非公平同步器(继承抽象同步器)
 */
static final class NonfairSync extends Sync {
    private static final long serialVersionUID = 7316153563782823691L;

    /**
     * 实现抽象同步器声明的lock()方法
     */
    final void lock() {
        // 直接通过CAS尝试获取锁,如果失败则调用acquire()方法
        if (compareAndSetState(0, 1))
            setExclusiveOwnerThread(Thread.currentThread());
        else
            acquire(1);
    }

    /**
     * 重写AQS的tryAcquire()方法,直接调用抽象同步器的nonfairTryAcquire()方法
     */
    protected final boolean tryAcquire(int acquires) {
        return nonfairTryAcquire(acquires);
    }
}

非公平模式下的ReentrantLock的加锁,将会调用非公平同步器的lock()方法,该方法先通过CAS尝试获取锁,如果获取失败则调用AQS的acquire()方法,该方法又会调用非公平同步器的tryAcquire()方法。

非公平模式下的ReentrantLock的解锁,将会调用AQS的release()方法,该方法又会调用抽象同步器提供的tryRelease()方法。


剖析公平同步器

/**
 * 公平的同步器(继承抽象同步器)
 */
static final class FairSync extends Sync {
    private static final long serialVersionUID = -3000897897090466540L;

    /**
     * 实现抽象同步器声明的lock()方法
     */
    final void lock() {
        // 直接调用acquire()方法
        acquire(1);
    }
    
	/**
     * 重写AQS的tryAcquire()方法
     */
    protected final boolean tryAcquire(int acquires) {
        final Thread current = Thread.currentThread();
        int c = getState();
        // 如果同步状态为0,同时当前线程就是等待队列中头节点的后继节点所封装的线程,或者当前等待队列为空或者只有一个节点,那么才会通过CAS尝试获取锁(这个判断用于保证是公平锁)
        if (c == 0) {
            if (!hasQueuedPredecessors() &&
                compareAndSetState(0, acquires)) {
                setExclusiveOwnerThread(current); // 将拥有同步资源的线程设置为当前线程
                return true;
            }
        }
        // 如果同步状态不为0,表示锁已经被线程所持有,同时如果当前线程就是拥有锁的线程,它还尝试获取锁,那么就累加同步状态的值,这种情况也返回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;
    }
}

公平模式下的ReentrantLock的加锁,将会调用公平同步器的lock()方法,该方法直接调用AQS的acquire()方法,该方法又会调用公平同步器的tryAcquire()方法。

公平模式下的ReentrantLock的解锁,将会调用AQS的release()方法,该方法又会调用抽象同步器提供的tryRelease()方法。


非公平模式下的总结

1.当线程要获取锁时,可以直接调用ReentrantLock的lock()和tryLock()方法。

2.如果调用lock()方法,那么将会调用非公平同步器的lock()方法,该方法会通过CAS尝试获取锁,如果获取锁成功则直接返回,做自己的事情,否则将会调用AQS的acquire()方法。

3.AQS的acquire()方法又会调用非公平同步器的tryAcquire()方法,该方法直接调用抽象同步器的nonfairTryAcquire()方法,如果同步状态的值为0,表示锁没有被线程所持有,则通过CAS尝试获取锁,如果获取锁成功则返回true,同时如果同步状态的值不为0,表示锁已经被线程所持有,如果当前线程就是拥有锁的线程,它还进行加锁,那么就累加同步状态的值,这种情况也会返回true,否则返回false。

4.如果调用tryLock()方法,那么将会调用抽象同步器的nonfairTryAcquire()方法,通过CAS尝试获取锁。

5.当线程要释放锁时,将会调用ReentrantLock的unlock()方法,该方法将会直接调用AQS的release()方法,该方法又会调用抽象同步器的tryRelease()方法,如果线程释放锁后,同步状态的值为0,则返回true,否则说明线程加了不止一把锁,然后它又没有释放全,这种情况会更新同步状态的值,然后返回false,只有当线程把它加的锁都释放后,tryRelease()方法才会返回true。


公平模式下的总结

1.当线程要获取锁时,可以直接调用ReentrantLock的lock()和tryLock()方法。

2.如果调用lock()方法,那么将会调用公平同步器的lock()方法,该方法直接调用AQS的acquire()方法。

3.AQS的acquire()方法又会调用公平同步器的tryAcquire()方法,同时该方法中只有当同步状态的值为0,同时当前线程是等待队列中头节点的后继节点所封装的线程,或者当前等待队列为空或只有一个节点时,才会通过CAS尝试获取锁,如果获取锁成功则返回true,同时如果同步状态的值不为0,表示锁已经被线程所持有,同时如果当前线程就是拥有锁的线程,它还进行加锁,那么累加同步状态的值,这种情况也会返回true,否则返回false。

4.如果调用tryLock()方法,那么将会调用抽象同步器的nonfairTryAcquire()方法,通过CAS尝试获取锁。

5.当线程要释放锁时,将会调用ReentrantLock的unlock()方法,该方法将会直接调用AQS的release()方法,该方法又会调用抽象同步器的tryRelease()方法,如果线程释放锁后,同步状态的值为0,则返回true,否则说明线程加了不止一把锁,然后它又没有释放全,这种情况会更新同步状态的值,然后返回false,只有当线程把它加的锁都释放后,tryRelease()方法才会返回true。


FAQ

关于ReentrantLock的tryLock()方法

ReentrantLock的tryLock()方法是非公平的,因为无论在什么模式下,该方法都会调用抽象同步器的nonfairTryAcquire()方法,因此当线程释放锁时,需要唤醒离头节点最近的同时等待状态不为CANCELLED的后继节点,然后在该节点尝试获取锁之前,被其他线程直接调用了tryLock()方法获取了锁,那么被唤醒的这个线程又只能再进入阻塞状态,这就是非公平的体现。

关于获取了锁的线程能否再进行加锁?

是可以的,因为无论在非公平还是公平模式下,tryAcquire()方法中都会有这么一个判断,如果同步状态的值不为0,表示锁已经被线程所持有,同时如果当前线程就是拥有锁的线程,它还进行加锁,那么累加同步状态的值,这种情况方法也会返回true,同时在抽象同步器的tryRelease()方法中,如果线程释放锁后,同步状态的值为0,则返回true,否则说明线程加了不止一把锁,然后它又没有释放全,这种情况会更新同步状态的值,然后返回false,只有当线程把它加的锁都释放后,tryRelease()方法才会返回true。

非公平锁是如何实现非公平的?

主要体现在非公平同步器的lock()方法,当线程要进行加锁时,并没有直接调用AQS的acquire()方法(虽然acquire也会调用tryAcquire),而是先通过CAS尝试获取锁,因此当线程释放锁时,需要唤醒离头节点最近的同时等待状态不为CANCELLED的后继节点,然后在该节点尝试获取锁之前,被其他线程直接调用了lock()方法获取了锁,那么被唤醒的这个线程又只能再进入阻塞状态,这就是非公平的体现。

公平锁是如何实现公平的?

主要体现在公平同步器的lock()和tryAcquire()方法,首先lock()方法直接调用AQS的acquire()方法,并没有像非公平同步器的lock()方法一样,先通过CAS尝试获取锁,然后在tryAcquire()方法中,只有当同步状态的值为0,同时当前线程就是等待队列中头节点的后继节点所封装的线程,或者当前等待队列为空或只有一个节点时,才会通过CAS尝试获取锁,因此当线程释放锁时,需要唤醒离头节点最近的同时等待状态不为CANCELLED的后继节点,然后在该节点尝试获取锁之前,被其他线程调用了lock()方法进行加锁,由于lock()方法直接调用AQS的acquire()方法,然后acquire()方法又调用公平同步器的tryAcquire()方法,虽然此时同步状态的值为0,但是当前线程并不是等待队列中头节点的后继节点所封装的线程,因此该线程也只能封装成Node节点,然后加入到等待队列当中。

posted @ 2020-09-06 23:50  辣鸡小篮子  阅读(381)  评论(0编辑  收藏  举报