欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

JUC并发编程基石AQS源码之结构篇-ReentrantLock

程序员文章站 2022-05-04 20:49:42
...

上篇文章JUC并发编程基石AQS源码之结构篇我们整体了解了JUC下加锁类的代码结构,这篇我们来看下ReentrantLock的代码结构,之后讲解AQS源码也主要以这个类为主。

实现Lock接口

首先是实现了Lock接口,实现了加锁方法lock(),但是这里没有直接调用AQS的acquire方法,往下看

public class ReentrantLock implements Lock, java.io.Serializable {
  
  public void lock() {
        sync.lock();
    }
  

定义内部类

先看一下内部类的继承关系,会发现比上一篇多了一层继承关系,我们来慢慢分析

JUC并发编程基石AQS源码之结构篇-ReentrantLock

定义了一个抽象内部类Sync继承了AQS,定义了一个抽象方法lock,但是会发现没有实现tryAcquire()方法,再往下看

public class ReentrantLock implements Lock, java.io.Serializable {

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

又一个内部类NonfairSync实现了Sync,实现了Sync类的lock()方法,并且实现了tryAcquire方法。lock方法里调用了AQS的acquire()方法。

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

到这我们就能梳理出大致的调用流程了,其中绿色部分代表需要自己实现

JUC并发编程基石AQS源码之结构篇-ReentrantLock

为什么多了一层结构?因为ReentrantLock有两种锁类型,公平锁和非公平锁

公平锁:不能插队,如果当前有线程在等待锁,则直接进入排队队列,等待在它之前排队的所有线程执行完后再去获取锁。

非公平锁:不管当前是否有其他线程在排队等待锁,先尝试获取一次锁,如果获取到则执行代码逻辑,否则进入排队队列,等待在它之前排队的所有线程执行完后再去获取锁。

而实现不同锁类型在代码上的体现就是对AQS的tryAcquire()方法的不同实现,有几种锁类型就有几个内部类。

JUC并发编程基石AQS源码之结构篇-ReentrantLock

再看上面这张图就会明白,NonfairSync代表了非公平锁的实现。FairSync代表了公平锁的实现。

加锁类型不同会有不同的tryAcquire()方法实现,其他的解锁等操作的逻辑是一致的,所以根据代码的重用性思想,就抽象出了Sync类。所以我们看Sync类只把加锁的方法抽象了,其他的都是具体的实现。

再看一下公平锁FairSync的代码,也只是实现了加锁的方法。

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

ReentrantLock默认的锁方式为非公平锁,构造方法中新建了NonfairSync的实例。所以在抽象类Sync默认给出了非公平锁的实现方法nonfairTryAcquire()。

//ReentrantLock构造方法
public ReentrantLock() {
    sync = new NonfairSync();
}

到此ReentrantLock类的结构我们大体都了解了,这样我们再去看源码才会有的放矢,知道为什么会跳到这个类的这个方法来执行了,这也是看懂AQS源码的第一个关键。

如有不实,还望指正

相关标签: java基础