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的对比

对比项 ReentrantLock synchronized
锁获取方式 显式调用 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 和公平性策略,可以实现复杂的线程协作逻辑。然而,其手动管理锁的特性也要求开发者更谨慎地编写代码,避免死锁和资源泄漏。

你可能感兴趣的:(java知识点,java,开发语言)