ReentrantLock是如何基于AQS实现的

ReentrantLock是一个可重入的互斥锁,基于AQS实现,它具有与使用 synchronized 方法和语句相同的一些基本行为和语义,但功能更强大。

lock和unlock

ReentrantLock 中进行同步操作都是从lock方法开始。lock获取锁,进行一系列的业务操作,结束后使用unlock释放锁。
    private final ReentrantLock lock = new ReentrantLock();
    public void sync(){
        lock.lock();
        try {
            // ... method body
        } finally {
          lock.unlock()
        }
    }

lock

ReentrantLock 中lock的实现是通过调用AQS的AbstractQueuedSynchronizer#acquire方法实现。
    public final void acquire(int arg) {
        //尝试获取锁
        if (!tryAcquire(arg) &&
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
            selfInterrupt();
    }

根据之前介绍的模板方法模式,对于锁的获取tryAcquire是在ReentrantLock中实现的。而非公平锁中的实际实现方法为nonfairTryAcquire。

ReentrantLock#nonfairTryAcquire
protected final boolean tryAcquire(int acquires) {
    return nonfairTryAcquire(acquires);
}
 
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;

在获取锁的逻辑中首先是尝试以cas方式获取锁,如果获取失败则表示锁已经被线程持有。

再判断持有该锁的线程是否为当前线程,如果是当前线程就将state的值加1,在释放锁是也需要释放多次。这就是可重入锁的实现。

如果持有锁的线程并非当前线程则这次加锁失败,返回false。加锁失败后将调用AbstractQueuedSynchronizer#acquireQueued(addWaiter(Node.EXCLUSIVE), arg)。

首先会调用addWaiter方法将该线程入队。
private Node addWaiter(Node mode) {
    Node node = new Node(Thread.currentThread(), mode);
    Node pred = tail;
    if (pred != null) {
        node.prev = pred;
        if (compareAndSetTail(pred, node)) {
            pred.next = node;
            return node;
        }
    }
    enq(node);
    return node;
}

mode是指以何种模式的节点入队,这里传入的是Node.EXCLUSIVE(独占锁)。首先将当前线程包装为node节点。然后判断等待队列的尾节点是否为空,如果不为空则通过cas的方式将当前节点接在队尾。如果tail为空则执行enq方法。

AbstractQueuedSynchronizer#enq
private Node enq(final Node node) {
    for (;;) {
        Node t = tail;
        if (t == null) { // Must initialize
            if (compareAndSetHead(new Node()))
                tail = head;
        } else {
            node.prev = t;
            if (compareAndSetTail(t, node)) {
                t.next = node;
                return t;
            }
        }
    }
}

enq方法通过for(;;)无限循环的方式将node节点设置到等待队列的队尾(队列为空时head和tail都指向当前节点)。

综上可知addWaiter方法的作用是将竞争锁失败的节点放到等待队列的队尾。

等待队列中的节点也并不是什么都不做,这些节点也会不断的尝试获取锁,逻辑在acquireQueued中实现。

AbstractQueuedSynchronizer#acquireQueued
final boolean acquireQueued(final Node node, int arg) {
    boolean failed = true;
    try {
        boolean interrupted = false;
        for (;;) {
            final Node p = node.predecessor();
            if (p == head && tryAcquire(arg)) {
                setHead(node);
                p.next = null; // help GC
                failed = false;
                return interrupted;
            }
            if (shouldParkAfterFailedAcquire(p, node) &&
                parkAndCheckInterrupt())
                interrupted = true;
        }
    } finally {
        if (failed)
            cancelAcquire(node);
    }
}

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/c5a9fd4c35c255e29d7a64eebf9138e2.html