ReentrantLock源码分析

ReentrantLock 是 Java 并发库中的一个重要类,它提供了比 synchronized 更灵活的锁机制。ReentrantLock 是一个互斥锁,具有与使用 synchronized 方法和语句所访问的隐式监视器锁类似的语义,但功能更强大。

功能结构

ReentrantLock 基于 AQS(AbstractQueuedSynchronizer)框架,通过继承 AQS 并实现其方法来管理锁的状态。有以下核心点:

  1. 状态管理ReentrantLock 使用一个整型变量来表示锁的状态,0 表示锁未被任何线程持有,大于 0 表示锁被某个线程持有,并且数值表示重入的次数。
  2. 锁的获取:当线程调用 lock() 方法时,会尝试通过 CAS(Compare-And-Swap)操作将锁状态从 0 改为 1。如果成功,表示该线程获取到了锁,并将当前线程设置为锁的拥有者。如果失败,表示锁已经被其他线程持有,当前线程会进入自旋等待或阻塞状态。
  3. 锁的释放:当线程调用 unlock() 方法时,会释放锁,即将锁状态减 1。如果状态变为 0,表示没有线程持有锁,此时会唤醒等待队列中的一个线程(如果是公平锁,则唤醒等待最久的线程;如果是非公平锁,则可能唤醒任意一个等待的线程)。
  4. 重入性:由于 ReentrantLock 支持重入,即同一个线程可以多次获取锁而不会被阻塞。这是通过维护一个计数器来实现的,每次线程获取锁时计数器加 1,每次释放锁时计数器减 1。只有当计数器变为 0 时,才表示该线程完全释放了锁。
  5. 公平性ReentrantLock 提供了公平锁和非公平锁两种实现。公平锁会严格按照线程请求锁的顺序来分配锁,而非公平锁则不会。这主要影响线程获取锁的策略和性能。
  6. LockSupportReentrantLock使用LockSupportpark()unpark()方法来阻塞和唤醒线程。当一个线程无法获取锁时,它会被park()阻塞;当锁被释放时,通过unpark()来唤醒等待的线程。

ReentrantLock 的状态管理主要是通过内部类 Sync 实现的,这个内部类继承自 AbstractQueuedSynchronizer(AQS)。AQS 提供了一个整型的 state 字段来表示同步状态,ReentrantLock 利用这个字段来表示锁是否被持有以及重入的次数。

ReentrantLock 内部的 Sync 类继承自 AbstractQueuedSynchronizer(AQS)。Sync 类提供了锁的基础实现,并定义了几个核心方法来控制锁的获取和释放。

Sync 类中的关键方法:

  1. tryAcquire(int acquires):

    • 作用:尝试获取锁。
    • 参数 acquires 通常表示尝试获取的锁的数量,对于 ReentrantLock 而言,这个值通常是1。
    • 如果成功获取锁,则返回 true;否则,返回 false
    • 这个方法在AQS中是一个需要由子类实现的方法,Sync 类提供了具体的实现逻辑。
  2. tryRelease(int releases):

    • 作用:尝试释放锁。
    • 参数 releases 表示要释放的锁的数量,对于 ReentrantLock 而言,这个值通常是1。
    • 如果成功释放锁,并且锁的状态变为0(即完全释放),则返回 true;否则,返回 false
    • 这个方法同样需要在AQS的子类中实现,Sync 类提供了具体逻辑。
  3. isLocked():

    • 作用:判断锁是否被某个线程持有。
    • 返回 true 如果锁被持有,否则返回 false
  4. isHeldExclusively():

    • 作用:判断当前线程是否独占持有锁。
    • 如果当前线程是锁的独占拥有者,则返回 true;否则,返回 false

除了这些方法,Sync 类还可能需要实现AQS中的其他一些方法,如 tryAcquireSharedtryReleaseShared,但对于 ReentrantLock 这种独占锁来说,这些方法通常不需要实现,因为 ReentrantLock 不支持共享锁模式。

Sync 类还有两个子类:NonfairSyncFairSync,分别对应非公平锁和公平锁的实现。这两个子类主要重写了 tryAcquire 方法以实现不同的锁获取策略。

  • NonfairSync: 在非公平锁中,tryAcquire 方法允许插队,即当前线程可以不管等待队列中的其他线程而直接尝试获取锁。

  • FairSync: 在公平锁中,tryAcquire 方法会检查等待队列中是否有其他线程在等待,如果有,则当前线程会排队等待,遵循FIFO(先进先出)原则。

总的来说,Sync 类及其子类提供了 ReentrantLock 的核心同步机制,通过实现AQS中定义的方法来控制锁的获取、释放和状态管理。

ReentrantLock核心方法

构造方法

1
2
3
4
5
6
7
public ReentrantLock() {
    sync = new NonfairSync(); // 默认非公平锁
}

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

这里定义了公平锁和非公平锁的实现。公平锁会按照线程请求锁的顺序来分配锁,而非公平锁则不会。

lock() 方法(以非公平锁为例)

1
2
3
4
5
6
final void lock() {
    if (compareAndSetState(0, 1)) // 尝试获取锁
        setExclusiveOwnerThread(Thread.currentThread()); // 设置当前线程为锁的拥有者
    else
        acquire(1); // 如果获取锁失败,则进入自旋等待或阻塞状态
}

这个方法尝试获取锁,如果当前没有线程持有锁,则当前线程会获取到锁并成为锁的拥有者。如果锁已经被其他线程持有,则当前线程会尝试自旋等待或进入阻塞状态。

unlock() 方法

1
2
3
public void unlock() {
    sync.release(1); // 释放锁
}

这个方法用于释放锁,使得其他等待的线程有机会获取到锁。

Sync TryAcquire

tryAcquire 方法尝试获取锁。以下是 NonfairSync(非公平锁)的实现示例,因为非公平锁和公平锁的主要区别在于获取锁的策略,而释放锁的逻辑是相同的。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
final boolean nonfairTryAcquire(int acquires) {
    final Thread current = Thread.currentThread(); // 获取当前线程
    int c = getState(); // 获取锁的状态(重入次数)
    if (c == 0) { // 如果锁未被持有(状态为0)
        if (compareAndSetState(0, acquires)) { // 尝试通过CAS操作设置锁状态
            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; // 获取锁失败
}

Sync TryRelease

tryRelease 方法尝试释放锁,并减少锁的重入计数。这个方法在公平锁和非公平锁中的实现是相同的。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
protected final boolean tryRelease(int releases) {
    int c = getState() - releases; // 减少重入次数
    if (Thread.currentThread() != getExclusiveOwnerThread()) // 检查当前线程是否是锁的拥有者
        throw new IllegalMonitorStateException();
    boolean free = false;
    if (c == 0) { // 如果锁的状态减为0,表示锁完全释放
        free = true;
        setExclusiveOwnerThread(null); // 清除锁的拥有者
    }
    setState(c); // 更新锁的状态
    return free; // 返回是否完全释放了锁
}

IsLocked 和 IsHeldExclusively

isLockedisHeldExclusively 方法用于检查锁的状态和拥有者。

1
2
3
4
5
6
7
8
9
// 判断锁是否被持有(无论被哪个线程)
public boolean isLocked() {
    return getState() != 0;
}

// 判断锁是否由当前线程独占持有
protected final boolean isHeldExclusively() {
    return getState() != 0 && Thread.currentThread() == getExclusiveOwnerThread();
}

这些核心方法通过操作AQS的内部状态(state)和独占线程(exclusiveOwnerThread)来管理锁。state 是一个整数,表示锁被持有的次数(对于重入锁,它表示重入的次数)。exclusiveOwnerThread 是一个 Thread 对象,表示当前持有锁的线程。

注意,这里的源码只是简化和注释版本,用于说明核心方法的实现原理。实际的 ReentrantLock 源码包含更多的错误处理、性能优化和同步机制。如果需要查看完整的实现,请查阅Java标准库中的源代码。