Skip to content
清晨的一缕阳光
返回

ReentrantLock 原理与实战

ReentrantLock 原理与实战

ReentrantLock 是基于 AQS 实现的可重入锁,提供更灵活的锁操作,支持公平/非公平、中断响应和条件变量。

一、核心特性

1.1 与 synchronized 对比

特性synchronizedReentrantLock
实现层级JVM 内置(字节码)JDK API(Lock 接口)
公平性非公平(默认)支持公平/非公平
中断响应❌ 不支持✅ 支持 lockInterruptibly()
超时获取❌ 不支持✅ 支持 tryLock(timeout)
条件变量wait/notify(单一)Condition(多个)
性能JDK 1.8+ 优化后高高并发场景更优
使用便捷简单(自动加解锁)需手动 try/finally

1.2 基本使用

ReentrantLock lock = new ReentrantLock();

lock.lock();
try {
    // 临界区代码
} finally {
    lock.unlock();
}

二、实现原理

2.1 继承 AQS

public class ReentrantLock implements Lock, java.io.Serializable {
    // 内部同步器
    private final Sync sync;
    
    // 抽象同步器(继承 AQS)
    abstract static class Sync extends AbstractQueuedSynchronizer {
        // state 表示锁的重入次数
    }
    
    // 非公平锁
    static final class NonfairSync extends Sync {
        protected final void lock() {
            if (compareAndSetState(0, 1))
                setExclusiveOwnerThread(Thread.currentThread());
            else
                acquire(1);
        }
    }
    
    // 公平锁
    static final class FairSync extends Sync {
        protected final void lock() {
            acquire(1);
        }
    }
}

2.2 state 状态

// state 表示锁的重入次数
// state = 0:锁空闲
// state > 0:锁被占用,值为重入次数

// 获取锁
if (compareAndSetState(0, 1)) {
    setExclusiveOwnerThread(Thread.currentThread());
}

// 重入
if (current == getExclusiveOwnerThread()) {
    setState(getState() + 1);
    return true;
}

// 释放锁
int c = getState() - 1;
setState(c);
if (c == 0) {
    setExclusiveOwnerThread(null);
}

2.3 非公平锁实现

final void lock() {
    // 1. 直接 CAS 尝试获取(插队)
    if (compareAndSetState(0, 1))
        setExclusiveOwnerThread(Thread.currentThread());
    else
        // 2. 失败则加入队列
        acquire(1);
}

protected final boolean tryAcquire(int acquires) {
    return nonfairTryAcquire(acquires);
}

final boolean nonfairTryAcquire(int acquires) {
    final Thread current = Thread.currentThread();
    int c = getState();
    
    if (c == 0) {
        // CAS 获取锁
        if (compareAndSetState(0, acquires)) {
            setExclusiveOwnerThread(current);
            return true;
        }
    } else if (current == getExclusiveOwnerThread()) {
        // 重入
        int nextc = c + acquires;
        setState(nextc);
        return true;
    }
    return false;
}

2.4 公平锁实现

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;
}

关键差异

// 公平锁增加检查
if (!hasQueuedPredecessors()) {
    // 队列中无等待线程才尝试获取
}

三、条件变量(Condition)

3.1 与 wait/notify 对比

// synchronized + wait/notify
synchronized (lock) {
    while (condition) {
        lock.wait();
    }
    // 业务逻辑
    lock.notify();
}

// ReentrantLock + Condition
lock.lock();
try {
    while (condition) {
        condition.await();
    }
    // 业务逻辑
    condition.signal();
} finally {
    lock.unlock();
}

3.2 多条件支持

ReentrantLock lock = new ReentrantLock();
Condition notFull = lock.newCondition();
Condition notEmpty = lock.newCondition();

// 生产者
lock.lock();
try {
    while (count == items.length)
        notFull.await();
    // 生产
    notEmpty.signal();
} finally {
    lock.unlock();
}

// 消费者
lock.lock();
try {
    while (count == 0)
        notEmpty.await();
    // 消费
    notFull.signal();
} finally {
    lock.unlock();
}

3.3 Condition 实现

// await() - 释放锁并加入条件队列
public final void await() throws InterruptedException {
    Node node = addConditionWaiter();
    int savedState = fullyRelease(node);
    int interruptMode = 0;
    
    while (!isOnSyncQueue(node)) {
        LockSupport.park(this);
        // 被唤醒后检查中断
    }
    
    acquireQueued(node, savedState);
}

// signal() - 转移到同步队列
public final void signal() {
    Node first = firstWaiter;
    if (first != null)
        doSignal(first);
}

四、中断响应

4.1 lockInterruptibly()

public void lockInterruptibly() throws InterruptedException {
    sync.acquireInterruptibly(1);
}

// AQS 实现
public final void acquireInterruptibly(int arg)
        throws InterruptedException {
    if (Thread.interrupted())
        throw new InterruptedException();
    
    if (!tryAcquire(arg))
        doAcquireInterruptibly(arg);
}

4.2 使用场景

// 可中断的锁获取
try {
    lock.lockInterruptibly();
    // 业务逻辑
} catch (InterruptedException e) {
    // 处理中断
    Thread.currentThread().interrupt();
} finally {
    if (lock.isHeldByCurrentThread())
        lock.unlock();
}

4.3 与 synchronized 对比

// synchronized 无法响应中断
synchronized (lock) {
    // 阻塞等待,无法中断
}

// ReentrantLock 可中断
lock.lockInterruptibly();

五、超时获取

5.1 tryLock(timeout)

// 尝试获取锁,超时返回
if (lock.tryLock(1, TimeUnit.SECONDS)) {
    try {
        // 业务逻辑
    } finally {
        lock.unlock();
    }
} else {
    // 超时处理
    System.out.println("获取锁超时");
}

5.2 非阻塞尝试

// 立即尝试获取
if (lock.tryLock()) {
    try {
        // 业务逻辑
    } finally {
        lock.unlock();
    }
} else {
    // 获取失败,执行其他逻辑
}

六、最佳实践

6.1 必须 finally 解锁

// ❌ 错误示例
lock.lock();
// 业务逻辑(异常时不解锁)

// ✅ 正确示例
lock.lock();
try {
    // 业务逻辑
} finally {
    lock.unlock();
}

6.2 选择公平锁

// 默认非公平锁(吞吐量大)
ReentrantLock lock = new ReentrantLock();

// 公平锁(避免饥饿)
ReentrantLock fairLock = new ReentrantLock(true);

6.3 避免死锁

// 固定顺序获取锁
public void transfer(Account from, Account to) {
    int fromHash = System.identityHashCode(from);
    int toHash = System.identityHashCode(to);
    
    if (fromHash < toHash) {
        from.lock.lock();
        to.lock.lock();
    } else {
        to.lock.lock();
        from.lock.lock();
    }
    try {
        // 转账逻辑
    } finally {
        from.lock.unlock();
        to.lock.unlock();
    }
}

6.4 读写锁(ReentrantReadWriteLock)

ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock();
ReadWriteLock readLock = rwLock.readLock();
ReadWriteLock writeLock = rwLock.writeLock();

// 读操作(共享)
readLock.lock();
try {
    return data;
} finally {
    readLock.unlock();
}

// 写操作(独占)
writeLock.lock();
try {
    data = newValue;
} finally {
    writeLock.unlock();
}

七、总结

ReentrantLock 核心要点:

特性说明优势
可重入同一线程可多次获取避免死锁
公平性支持公平/非公平灵活选择
中断响应lockInterruptibly()可中断等待
超时获取tryLock(timeout)避免无限等待
条件变量多 Condition精确控制

synchronized 优先,特殊场景(公平、中断、多条件)用 ReentrantLock。


分享这篇文章到:

上一篇文章
CompletableFuture 异步编程实战
下一篇文章
ConcurrentHashMap 原理详解