ReentrantLock

给代码加锁可以使用synchronized进行加锁,这种加锁和释放锁是隐式的。也可以使用ReentrantLock进行显示的加锁和释放锁,分别对应lock和unlock方法。
ReentrantLock是一个互斥锁,分为公平锁和非公平锁。当我们创建一个ReentrantLock时默认是非公平锁,因为非公平锁的效率比公平锁的效率高,但也带来一个问题是非公平锁中的线程可能会有饿死的情况。

非公平锁

ReentrantLock中维护了一个内部类Sync,该内部类继承了AQS,实现了AQS中的tryAcquire、tryRelease等方法;而非公平锁类是NonfairSync,该类继承了Sync,实现了lock和tryAcquire方法。

lock

非公平锁NonfairSync中的lock方法如下

final void lock() {
    if (compareAndSetState(0, 1))
        setExclusiveOwnerThread(Thread.currentThread());
    else
        acquire(1);
}
  • 首先使用CAS的方式设置state值为1,如果设置成功,则调用setExclusiveOwnerThread方法,将当前线程设置成独占锁
  • 如果设置失败,调用acquire方法,这里调用的是AQS中的acquire方法,这个方法在之前的AQS中介绍了,在acquire方法中会首先调用子类的tryAcquire方法,这里就会调用NonfairSync类的tryAcquire方法

tryAcquire

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

这个方法很简单,直接调用nonfairTryAcquire方法将需要设置的state值传入。这个方法的具体实现是在Sync类中

nonfairTryAcquire

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;
}
  • 获取当前线程,获取state值c
  • 如果state为0,说明是可以获取锁的,那么使用CAS的方式将state的值从0改为1,如果成功,那么将当前线程设置为独占锁,并返回true
  • 如果state不为0或者CAS设置state失败,那么就说明有线程获取了锁,从getExclusiveOwnerThread中获取线程和当前相比
    • 如果不相等,说明不是同一个锁,返回false,接着进入AQS,将当前线程加入到同步队列中
    • 如果相等,说明是重入锁,将c和acquire相加得到nextc,如果nextc小于0,说明不合法,直接抛出异常;如果大于0,则将最新的值写入state并返回true,获取锁成功

公平锁

公平锁的类是FairSync,继承了Sync类,该类中同样只有lock和tryAcquire方法。

lock

公平锁的lock和非公平锁的lock方法相比不同之处在于:公平锁lock方法直接调用的acquire方法,并没有CAS设置state这一步

final void lock() {
    acquire(1);
}

tryAcquire

当使用公平锁获取锁的时候会调用tryAcquire方法,该方法和非公平锁大体逻辑一致,不同之处在于:如果state为0,说明当前线程是可以获取锁的,但在获取锁之前需要进行一个判断:是否有任何线程等待获取锁的时间超过当前线程的判断

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

public final boolean hasQueuedPredecessors() {

    Node t = tail; // Read fields in reverse initialization order
    Node h = head;
    Node s;
    return h != t &&
        ((s = h.next) == null || s.thread != Thread.currentThread());
}

这里主要说hasQueuedPredecessors方法:

  • 如果h 等于 t 说明队列中没有节点,返回false,那么!hasQueuedPredecessors()就为true,说明该线程是可以获取锁的
  • 如果h不等于t(说明同步队列中有节点) && (如果头结点的后继s不为null并且s的线程和当前线程相等,则返回false,表示表示当前线程就是等待时间最长的线程),返回false

以上就是公平锁和非公平锁获取锁的过程

释放锁

公平锁和非公平锁释放锁的逻辑都是在Sync中,首先通过ReentrantLock的unlock方法释放锁,调用的是AQS的release方法,在AQS的release方法中会调用子类的tryRelease方法,这时候就会调用Sync类的tryRelease方法

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;
}
  • 传入需要释放的state值,获取AQS中的state值减去传入的值得到c
  • 判断当前线程是否是独占锁的线程,如果不是抛出异常
  • 如果相等,判断c是否为0,如果为0,将独占锁的线程置为null
  • 修改state值,返回free,如果为true,说明可以下次别的线程可以获取锁,如果为false,则不能,因为该线程还占有着锁,这是一个重入锁

当然该类也支持中断式获取锁以及超时获取锁,原理大致相同,底层都是依赖AQS。

ReentrantLock的等待唤醒机制使用的是Condition,底层调用的是AQS的内部类ConditionObject中的方法。这些都在AQS中说过

以上就是ReentrantLock的整体逻辑

posted @ 2021-07-13 09:57  扭不动的奥利奥  阅读(143)  评论(0)    收藏  举报