ReentrantLock类详解


ReentrantLock类详解


1. 核心概念

ReentrantLock 是Java中基于 java.util.concurrent.locks.Lock 接口实现的可重入互斥锁,提供比 synchronized 更灵活的锁控制机制。关键特性包括:

  • 可重入性:同一线程可多次获取同一把锁。
  • 公平性选择:支持公平锁(先请求先获取)和非公平锁(默认,允许插队)。
  • 可中断锁获取:线程等待锁时可响应中断。
  • 超时尝试锁:指定时间内尝试获取锁,避免无限等待。

2. 核心方法与使用

(1) 基础用法
import java.util.concurrent.locks.ReentrantLock;

public class ReentrantLockDemo {
    private final ReentrantLock lock = new ReentrantLock();
    
    public void safeMethod() {
        lock.lock();  // 获取锁
        try {
            // 临界区代码
            System.out.println("线程安全操作");
        } finally {
            lock.unlock();  // 必须手动释放锁
        }
    }
}
  • 关键点
    • 必须使用 try-finally 确保锁的释放,防止死锁。
    • 锁的获取与释放必须成对出现,否则导致其他线程永久阻塞。

(2) 高级方法
方法作用
boolean tryLock()尝试非阻塞获取锁,成功返回 true,失败立即返回 false
boolean tryLock(long timeout, TimeUnit unit)超时等待获取锁,超时后返回 false
void lockInterruptibly()可中断地获取锁,等待过程中可响应中断。
boolean isHeldByCurrentThread()判断当前线程是否持有锁。
int getHoldCount()返回当前线程持有该锁的次数(重入次数)。

3. 公平锁与非公平锁

(1) 构造函数
// 默认非公平锁(性能更高,允许线程插队)
ReentrantLock nonFairLock = new ReentrantLock(); 

// 公平锁(按请求顺序分配锁,减少线程饥饿)
ReentrantLock fairLock = new ReentrantLock(true); 
(2) 对比
特性非公平锁公平锁
性能高(减少线程切换开销)低(需维护等待队列)
线程饥饿可能发生(新线程可能插队)避免线程饥饿
适用场景高并发、锁持有时间短严格要求顺序的业务(如订单处理)

4. 与synchronized的对比

对比项ReentrantLocksynchronized
锁获取方式显式调用 lock()unlock()隐式获取和释放(代码块或方法)
可中断性支持(lockInterruptibly()不支持
超时机制支持(tryLock(timeout)不支持
公平性可配置仅非公平
锁绑定条件支持多个 Condition一个对象只能绑定一个等待队列
性能高并发下更优优化后接近(低竞争场景更简单)

5. Condition条件变量

Condition 用于替代 Object.wait()/notify(),实现更精准的线程等待与唤醒。

(1) 基本用法
private final ReentrantLock lock = new ReentrantLock();
private final Condition condition = lock.newCondition();

public void await() throws InterruptedException {
    lock.lock();
    try {
        condition.await();  // 释放锁并等待
    } finally {
        lock.unlock();
    }
}

public void signal() {
    lock.lock();
    try {
        condition.signal(); // 唤醒一个等待线程
    } finally {
        lock.unlock();
    }
}
(2) 多条件示例(生产者-消费者模型)
private final Condition notFull = lock.newCondition();  // 队列未满条件
private final Condition notEmpty = lock.newCondition(); // 队列非空条件

// 生产者
public void put(Object item) throws InterruptedException {
    lock.lock();
    try {
        while (queue.isFull()) {
            notFull.await(); // 等待队列未满
        }
        queue.add(item);
        notEmpty.signal();  // 唤醒消费者
    } finally {
        lock.unlock();
    }
}

// 消费者
public Object take() throws InterruptedException {
    lock.lock();
    try {
        while (queue.isEmpty()) {
            notEmpty.await(); // 等待队列非空
        }
        Object item = queue.remove();
        notFull.signal();    // 唤醒生产者
        return item;
    } finally {
        lock.unlock();
    }
}

6. 最佳实践与注意事项

  1. 避免锁泄漏:确保 unlock()finally 块中执行。
  2. 减少锁持有时间:仅在必要代码段加锁,提升并发性能。
  3. 优先使用非公平锁:除非业务严格要求顺序。
  4. 合理使用Condition:避免复杂条件逻辑导致代码难以维护。

总结

ReentrantLock 提供了比 synchronized 更精细的锁控制能力,适用于高并发、需要灵活锁管理的场景。通过结合 Condition 和公平性策略,可以实现复杂的线程协作逻辑。然而,其手动管理锁的特性也要求开发者更谨慎地编写代码,避免死锁和资源泄漏。

<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
发出的红包

打赏作者

步行cgn

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值