C/C++教程

ReentrantLock源码分析

本文主要是介绍ReentrantLock源码分析,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

ReentrantLock是一个可重入的独占锁,内部使用AQS实现。state记录着持有锁的线程的进入同步代码块的次数。

1. AQS的基本实现Sync

Sync是一个抽象类,他没有重写tryAcquire方法,但是他多了一个nonfairTryAcquire方法,该方法是一个非公平获取锁资源的方法。提供了一个抽象的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
    }
}

1.1 nonfairTryAcquire

因为在ReentrantLock是支持公平锁和非公平锁的,这里的nonfairTryAcquire方法就是非公平获取锁资源的实现。我们可以看到分两种情况,一是当没有线程持有锁时,直接cas修改state,修改成功就获取到了锁,并将当前持有独占锁的线程设置为自己。二是判断当前自己是不是持有锁的线程(可能是重入),如果是就只需增加锁的重入次数。最后就是有其他线程占用了锁,返回false。

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

1.2 tryRelease

进行锁资源的释放,该方法是线程安全的,因为独占模式只有一个线程只有锁,也就只有一个线程可以释放锁资源。释放锁刚好更获取锁资源反着来,减少锁的重入次数,设置当前持有锁的线程为null,当state为0时,也就说明当前线程释放了锁资源,就返回true,从而唤醒后继节点。

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

2. 公平和非公平模式

可以通过有参构造方法,指定使用公平锁还是非公平锁,默认无参构造方法是非公平锁。

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

2.1 非公平模式

实现了上面我们说的Sync类型,补上对应两个需要实现的方法locktryAcquire,这里的tryAcquire直接调用我们上面说的非公平获取锁资源的方法nonfairTryAcquire。这里lock方法本来可以直接调用acquire方法,但是他在前面还是做了一次快速获取锁资源的尝试,直接casstate,如果成功就设置持有锁的线程是当前线程,如果失败就再走一次正常的逻辑。

非公平性:代码我们可以看到,主要线程获取到了锁资源,就表示该线程持有了锁,不关系队列中释放有排在其前面的线程在等待。优点很明显,就是吞吐高;缺点也很明显,会导致线程饥渴,一直等待无法获取到锁资源(因为会被其他线程抢)。

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

2.2 公平模式

也是实现了locktryAcquire方法,这里的lock方法就有了一点变化了,没有在lock里面之前获取锁资源了,而是全部交给了acquire来处理,这体现了公平性的一点,因为如果直接跟上面的非公平锁中lock一样获取锁资源的话,就没办法办证公平性了。tryAcquire中相比非公平锁,多了hasQueuedPredecessors方法的判断,该方法用来判断当前节点是否有前驱节点,如果有,就获取锁失败,也就是说来获取锁资源的线程必须保证他没有前驱节点,其他代码基本和非公平锁一样。

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

3. 实现了lock类

实现了统一的lock接口,我们在使用时,一般是使用lock接口中的方法进行加锁和解锁。这里的sync就是构造方法中创建的NonfairSyncFairSync,另外newCondition是创建一个条件队列对象。

public class ReentrantLock implements Lock, java.io.Serializable {
    ...
    public void lock() {
        sync.lock();
    }
    public void lockInterruptibly() throws InterruptedException {
       sync.acquireInterruptibly(1);
    }
    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);
    }
    public Condition newCondition() {
        return sync.newCondition();
    }
}    

4. 总结

ReentrantLock其实很简单,如果你看过AQS的源码分析,那ReentrantLock就是基于AQS实现一种独占(排他)可重入的锁。他提供了公平和非公平模式,公平性是在获取锁资源时,检查当前节点是否有前驱节点。最后是实现了标准的Lock接口,方便使用统一的lockunlock进行加锁和解锁。

这篇关于ReentrantLock源码分析的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!