Java ReentrantLock 源码剖析

217 阅读3分钟

本文源码基于 JDK 1.8

定义

ReentrantLock = re + entrant + lock,翻译过来就是重入锁, 它指的是一个线程能够对资源重复加锁。 synchronized 也能重复加锁,只是 synchronized 内的代码执行异常后会自动释放到 monitor 上的锁,而 ReentrantLock 需要手动释放(调用 unlock 方法)

另外,ReentrantLock 提供了公平锁与非公平锁的选择, 它们之间的区别主要就是看对锁的获取与获取锁的请求的顺序是否是一致的,选择公平锁时,等待时间最长的线程会最优先获取到锁,但是公平锁获取的效率通常比非公平锁要低

先来看一下 ReentrantLock 源码部分核心代码,具体细节先不管,后面会分析

public class ReentrantLock implements Lock, java.io.Serializable {
    abstract static class Sync extends AbstractQueuedSynchronizer {...}
    // 非公平锁
    static final class NonfairSync extends Sync {...}
    // 公平锁
    static final class FairSync extends Sync {...}
    
    private final Sync sync;
    // 默认非公平锁
    public ReentrantLock() {
        sync = new NonfairSync();
    }
    public ReentrantLock(boolean fair) {
        sync = fair ? new FairSync() : new NonfairSync();
    }
    
    public void lock() {
        sync.lock();
    }
    
    public void unlock() {
        sync.release(1);
    }
}

可以看到 ReentrantLock 的核心实现都是基于 Sync, 而且 Sync 还继承了 AbstractQueuedSynchronizer,也就是我们常说的 AQS。 如果对 AQS 还不太了解,建议先了解 AQS 再来阅读本文,强烈建议搭配Java AQS 详解观看

AQS 提供了 5 个模板方法

// 独占模式
protected boolean tryAcquire(int arg) {}
protected boolean tryRelease(int arg) {}
// 共享模式
protected int tryAcquireShared(int arg) {}
protected boolean tryReleaseShared(int arg) {}
// 该线程是否正在独占资源。只有用到condition才需要去实现它
protected boolean isHeldExclusively() {}

Sync

Sync 实现了 NonfairSyncFairSync 的公用方法,具体如下:

protected final boolean isHeldExclusively() {
    // 是否当前线程持有锁
    // getExclusiveOwnerThread() 返回当前独占模式锁的所有者线程
    return getExclusiveOwnerThread() == Thread.currentThread();
}

protected final boolean tryRelease(int releases) {
    int c = getState() - releases;
    if (Thread.currentThread() != getExclusiveOwnerThread())
        throw new IllegalMonitorStateException();
    // 是否完全释放锁
    boolean free = false;
    if (c == 0) {
        // 如果 c == 0,表明锁已完全释放,也没有重入锁了
        free = true;
        // 设置当前拥有独占访问权的线程为 NULL,真正释放锁
        setExclusiveOwnerThread(null);
    }
    setState(c);
    return free;
}

// 非公平锁的 tryAcquire 实现
// 非公平锁和公平锁的 tryAcquire 实现有差异,因此不能在这里直接重写 tryAcquire 方法
final boolean nonfairTryAcquire(int acquires) {
    final Thread current = Thread.currentThread();
    int c = getState();
    if (c == 0) {
        // 如果当前 state == 0,就使用 CAS 更新 state
        if (compareAndSetState(0, acquires)) {
            // 如果更新,设置独占模式锁的所有者为当前线程
            setExclusiveOwnerThread(current);
            return true;
        }
    }
    // 如果 state != 0,判断当前线程是不是独占模式锁的持有者
    else if (current == getExclusiveOwnerThread()) {
        // 如果是,就尝试更新 state,即重入了
        int nextc = c + acquires;
        if (nextc < 0) // overflow
            throw new Error("Maximum lock count exceeded");
        setState(nextc);
        return true;
    }
    return false;
}

NonfairSync

非公平锁是 ReentrantLock 的默认实现,也可手动指定 new ReentrantLock(false)

// 参考上面的 nonfairTryAcquire
protected final boolean tryAcquire(int acquires) {
    return nonfairTryAcquire(acquires);
}

// 因为是非公平锁,调用 lock 方法就先试下能不能抢到再说
final void lock() {
    // 如果 CAS 更新 state 成功,则代表抢到了锁,因为之前没有线程获取到锁
    if (compareAndSetState(0, 1))
        setExclusiveOwnerThread(Thread.currentThread());
    else
        // 核心方法,此实现在 AbstractQueuedSynchronizer 类
        acquire(1);
}

FairSync

创建非公平锁需要在创建 ReentrantLock 实例时手动指定 new ReentrantLock(true)

protected final boolean tryAcquire(int acquires) {
    final Thread current = Thread.currentThread();
    int c = getState();
    if (c == 0) {
        // 这里相对于非公平锁多了一个 hasQueuedPredecessors 方法
        // hasQueuedPredecessors 用来判断 CLH 队列中是否还有正在排队的线程
        // 因为是公平的锁,所以得先来后到,如果 CLH 队列中还有正在排队等候获取锁的线程就放弃获取锁
        // 非公平锁如果有排队的线程也会抢锁,所以判断 CLH 队列中是否有正在排队的线程没有意义
        // 如果 CLH 队列中没有在排队的线程了,则 CAS 更新 state
        // 如果更新成功,代表也没有其它线程获取到锁,就把锁的所有者设置为当前线程
        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;
}

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