ReentrantLock加锁、解锁源码解读

一、ReentrantLock类继承关系

二、方法

1、加锁:lock()

首先看一下方法调用线路图,RenntrantLock默认是非公平锁。

1、公平锁

final void lock() {
    // 调用AQS的acquire()
    acquire(1);
}

// 排它模式下,尝试获得锁
public final void acquire(int arg) {
    // tryAcquire 方法是需要实现类去实现的,实现思路一般都是 cas 给 state 赋值来决定是否能获得锁
    if (!tryAcquire(arg) &&
        // addWaiter 入参代表是排他模式
        acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
        selfInterrupt();
}

protected final boolean tryAcquire(int acquires) {
    		// 获取当前线程
            final Thread current = Thread.currentThread();
    		// 获取锁的状态,如果不为0,则说明锁别人持有
            int c = getState();
    		// 没人获得锁
            if (c == 0) {
                // hasQueuedPredecessors 是实现公平的关键
                // 会判断当前线程是不是队列中头结点的下一个节点(头节点是释放锁的节点)
                // 如果是(返回false),条件符合,可获得锁
                // 如果不是(返回true),则继续等待
                // compareAndSetState(0, acquires)自旋判断,如果锁的计数器不是0
                if (!hasQueuedPredecessors() &&
                    compareAndSetState(0, acquires)) {
                    setExclusiveOwnerThread(current);
                    return true;
                }
            }
    		// 如果当前线程不是线程持有者,则返回false
            else if (current == getExclusiveOwnerThread()) {
                int nextc = c + acquires;
                if (nextc < 0)
                    // 溢出检查
                    throw new Error("Maximum lock count exceeded");
                // 设置锁计数器值
                setState(nextc);
                return true;
            }
            return false;
        }

2、非公平锁

1、lock()
final void lock() {
    // CAS设置状态值
    if (compareAndSetState(0, 1))
        // CAS成功,将当前线程设置成获得锁的线程
        setExclusiveOwnerThread(Thread.currentThread());
    else
        // 调用AQS的acquire(),再次尝试获得锁
        // 失败会进入同步队列中
        acquire(1);
}

2、tryAcquire()
// 直接使用的是 Sync.nonfairTryAcquire 方法 
protected final boolean tryAcquire(int acquires) {
    return nonfairTryAcquire(acquires);
}
3、nonfairTryAcquire()

​ 此方法与公平锁的差别主要是此处并不会去队列中判断自己是不是最早请求该锁的线程,可以理解是现实生活中的插队。

final boolean nonfairTryAcquire(int acquires) {
            
    final Thread current = Thread.currentThread();
    // 获取同步器的状态      
    int c = getState();
    // 状态为0的话,说明同步器的锁没有人持有        
    if (c == 0) {
        // 尝试CAS获得该锁,将AQS的状态从0设置为1
        if (compareAndSetState(0, acquires)) {
            // 设置当前线程为线程锁持有者        
            setExclusiveOwnerThread(current);
            return true;
        }
    }
    // 锁已经被人持有,需要判断当前线程是不是锁的持有者;是的话,则状态值+1        
    else if (current == getExclusiveOwnerThread()) {
        int nextc = c + acquires;
        // 判断是否溢出了        
        if (nextc < 0) // overflow
            throw new Error("Maximum lock count exceeded");
        setState(nextc);
        return true;
    }
    // 抢锁失败,放入AQS阻塞队列
    return false;
}

2、释放锁:unlock()

unlock 释放锁的方法,底层调用的是 Sync 同步器的 release 方法,release 是 AQS 的方法,分成两步:

  1. 调用Sync类的release方法,释放不成功返回false,调用步骤2
  2. 释放成功后,从同步队列中唤醒头结点的下一个节点,让其竞争锁
protected final boolean tryRelease(int releases) {        
    int c = getState() - releases;
    // 如果不是线程持有者释放锁会抛出异常        
    if (Thread.currentThread() != getExclusiveOwnerThread())
        throw new IllegalMonitorStateException();
    boolean free = false;
    // 如果同步器状态为为0,则清空锁持有线程        
    if (c == 0) {
        free = true;
        setExclusiveOwnerThread(null);
    }
    // c不是0,当前线程已经拥有这个锁,即可重入锁,将重入次数-1.
    setState(c);
    return free;
}

tryRelease()方法是公平锁与非公平锁公用的。

<think>好的,我现在需要详细解释JavaReentrantLock加锁机制和使用方法。首先,用户提供的引用资料中提到了ReentrantLock的基本用法,比如lock()和unlock()方法,以及其与AQS(AbstractQueuedSynchronizer)的关系。我需要将这些信息整合起来,形成一个清晰的讲解结构。 首先,应该从ReentrantLock的基本概念入手,说明它是一个可重入的互斥锁,具有与synchronized相似的功能,但更灵活。然后,引用示例代码展示基本用法,比如在doSomething方法中加锁解锁的过程。这里要注意代码的正确性,比如在finally块中释放锁,确保异常时也能解锁。 接下来,需要深入加锁机制。根据引用[3],ReentrantLock的lock()方法调用了sync.lock(),而sync是继承自AQS的内部类。这里要区分公平锁和非公平锁的不同实现。非公平锁会直接尝试获取锁,如果失败则进入AQS队列;公平锁则会先检查队列是否有等待线程。需要详细解释这两个过程,并结合源码片段,如compareAndSetState(0,1)来展示CAS操作。 然后,可重入性的实现是关键点。要说明锁内部维护了一个计数器,记录线程重入的次数,每次lock()计数器加1,unlock()减1,直到为0时释放锁。这部分需要例子说明,比如同一个线程多次加锁的情况。 此外,使用注意事项也很重要,比如确保在finally中释放锁,避免死锁。还可以提到ReentrantLock的高级功能,如尝试获取锁、可中断锁等,但根据用户的问题,可能需要简要提及,保持重点在基本机制和用法。 最后,生成相关问题时要围绕ReentrantLock的特性、AQS的作用、公平与非公平锁的区别、使用注意事项等,确保问题能帮助用户进一步理解。 需要检查引用资料的正确性,比如引用[4]提到Doug Lea的贡献和AQS,可以用来支持AQS的讲解部分。同时,确保各部分引用标注正确,比如源码部分引用[3]和[4]。 现在组织内容的结构:概述、基本用法、加锁机制(公平与非公平)、可重入性、注意事项、相关问题。确保每个部分都有引用支持,并且语言简洁明了,符合用户要求的中文回答,使用正确的LaTeX格式,如代码块用反引号包裹,行内公式用$符号。</think>### ReentrantLock加锁机制及使用详解 #### 1. 基本概念 `ReentrantLock`是Java提供的可重入互斥锁,属于`java.util.concurrent.locks`包。与`synchronized`关键字相比,它支持**公平锁策略**、**可中断锁**和**超时获取锁**等特性[^4]。 #### 2. 基本用法 通过`lock()`加锁,`unlock()`释放锁,需在`finally`块中确保释放: ```java private ReentrantLock lock = new ReentrantLock(); public void doSomething() { lock.lock(); // 加锁 try { // 临界区代码 } finally { lock.unlock(); // 释放锁 } } ``` #### 3. 加锁机制 ##### 3.1 非公平锁(默认) 调用`lock()`时通过CAS操作尝试直接获取锁: ```java final void lock() { if (compareAndSetState(0, 1)) // CAS操作 setExclusiveOwnerThread(Thread.currentThread()); else acquire(1); // 进入AQS队列等待 } ``` 若失败则调用`acquire()`,进入AQS队列排队[^3]。 ##### 3.2 公平锁 构造函数传入`true`启用公平锁: ```java ReentrantLock fairLock = new ReentrantLock(true); ``` 公平锁会先检查AQS队列是否有等待线程,避免插队[^4]。 #### 4. 可重入性实现 通过`state`变量记录重入次数: ```java 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; setState(nextc); // 增加重入次数 return true; } return false; } ``` #### 5. 注意事项 1. **必须手动释放锁**:`unlock()`需在`finally`中调用,避免死锁 2. **避免锁嵌套**:重入次数需与释放次数严格匹配 3. **性能权衡**:公平锁保证顺序但吞吐量低,非公平锁反之 #### 6. 典型应用场景 - 需要细粒度控制的并发资源访问 - 实现带超时的锁获取(`tryLock(long timeout, TimeUnit unit)`) - 处理可中断的阻塞操作(`lockInterruptibly()`)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值