跳转到内容

ReentrantLock原理

来自代码酷

模板:Note

ReentrantLock原理[编辑 | 编辑源代码]

ReentrantLock是Java并发包(`java.util.concurrent.locks`)中提供的可重入互斥锁实现,相比`synchronized`关键字,它提供了更灵活的锁控制能力,支持公平性选择、可中断的锁获取、超时机制等高级特性。

核心特性[编辑 | 编辑源代码]

  • 可重入性:线程可以重复获取已持有的锁(通过计数器实现)
  • 公平性选择:支持公平锁(按等待顺序获取)和非公平锁(竞争获取)
  • 锁中断:通过`lockInterruptibly()`响应中断
  • 条件变量:通过`Condition`实现精细化的线程通信

底层实现原理[编辑 | 编辑源代码]

同步器(AQS)基础[编辑 | 编辑源代码]

ReentrantLock基于AbstractQueuedSynchronizer(AQS)框架实现,其核心是通过一个volatile整型变量`state`表示锁状态:

  • state=0:锁未被占用
  • state>0:锁被占用,数值表示重入次数

stateDiagram-v2 [*] --> Unlocked: state=0 Unlocked --> Locked: 线程获取锁 Locked --> Locked: 重入(state++) Locked --> Unlocked: 完全释放(state=0)

获取锁流程(非公平模式)[编辑 | 编辑源代码]

1. 尝试通过CAS修改state 2. 成功则设置独占线程为当前线程 3. 失败则进入AQS队列等待

释放锁流程[编辑 | 编辑源代码]

1. 减少重入计数器 2. 当state归零时:

  * 清除独占线程标记
  * 唤醒队列中的等待线程

代码示例[编辑 | 编辑源代码]

基础用法[编辑 | 编辑源代码]

import java.util.concurrent.locks.ReentrantLock;

public class Counter {
    private final ReentrantLock lock = new ReentrantLock();
    private int count = 0;

    public void increment() {
        lock.lock();  // 阻塞直到获取锁
        try {
            count++;
            System.out.println("Count: " + count);
        } finally {
            lock.unlock();  // 必须手动释放
        }
    }
}

模板:Tip

公平锁示例[编辑 | 编辑源代码]

ReentrantLock fairLock = new ReentrantLock(true);  // 公平锁

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

与synchronized对比[编辑 | 编辑源代码]

特性 ReentrantLock synchronized
实现方式 API级实现 JVM内置实现
锁获取方式 可非阻塞尝试(tryLock) 仅阻塞获取
公平性 可配置 非公平
条件变量 支持多个Condition 单一等待队列
锁释放 必须显式调用unlock() 自动释放

高级特性[编辑 | 编辑源代码]

锁中断[编辑 | 编辑源代码]

public void performTaskWithTimeout() throws InterruptedException {
    ReentrantLock lock = new ReentrantLock();
    if (lock.tryLock(1, TimeUnit.SECONDS)) {  // 超时等待
        try {
            // 临界区
        } finally {
            lock.unlock();
        }
    } else {
        System.out.println("获取锁超时");
    }
}

条件变量[编辑 | 编辑源代码]

class BoundedBuffer {
    final ReentrantLock lock = new ReentrantLock();
    final Condition notFull = lock.newCondition(); 
    final Condition notEmpty = lock.newCondition();

    void put(Object x) throws InterruptedException {
        lock.lock();
        try {
            while (/* 缓冲区满 */) {
                notFull.await();  // 释放锁并等待
            }
            // 放入数据
            notEmpty.signal();  // 唤醒消费者
        } finally {
            lock.unlock();
        }
    }
}

性能考量[编辑 | 编辑源代码]

  • 非公平锁(默认)通常吞吐量更高,但可能导致线程饥饿
  • 公平锁保证顺序性,但增加上下文切换开销
  • 在激烈竞争场景下,ReentrantLock性能可能优于synchronized

常见问题[编辑 | 编辑源代码]

模板:Q&A

模板:Q&A

实际应用案例[编辑 | 编辑源代码]

电商库存扣减系统

  • 使用ReentrantLock保护共享库存数据
  • 通过tryLock实现超时控制,避免系统雪崩
  • 使用Condition实现库存不足时的等待/通知机制

页面模块:Message box/ambox.css没有内容。

总结[编辑 | 编辑源代码]

ReentrantLock提供了比synchronized更精细的锁控制,适合需要高级特性的并发场景。理解其AQS实现原理有助于正确使用和性能调优。在简单场景下,仍推荐优先使用synchronized。