Java ReentrantLock锁机制详解与可重入实现

阿橙
阿橙
发布时间:2026-10-13 | 4人浏览过
原创

自 Java 5 起,标准库提供了成熟的 ReentrantLock 等锁实现,替代了手写复杂的同步逻辑。本文将基于 Lock 接口特性,解析手动实现可重入锁的核心原理,展示临界区代码保护及异常处理的最佳实践。

结果与目标

掌握 java.util.concurrent.locks.Lock 的基本操作规范,理解如何实现具备可重入特性的线程锁,以及为何必须在异常环境下确保锁被释放。

准备条件

  • JDK 环境(建议使用 JDK 8 及以上版本以获取完整的并发工具支持)。
  • 对 Java 基础同步关键字 synchronized 有基本了解。

真实操作顺序

第1步:使用 Lock 进行资源同步

目的:利用显式 Lock 对象替代同步块,实现对共享变量的安全访问。

入口:定义一个包含锁对象的类。

动作:在访问临界区前调用 lock(),结束后调用 unlock()。以下计数器示例展示了基本的加锁流程:

public class Counter {
  private Lock lock = new ReentrantLock(); // 实际使用时推荐使用标准实现
  private int count = 0;

  public int inc() {
    lock.lock();
    try {
      int newCount = ++count;
      return newCount;
    } finally {
      lock.unlock(); // 推荐在finally中释放,防止遗漏
    }
  }
}

完成状态:在同一时刻只有一个线程能执行 ++count 操作,确保数据一致性。

第2步:解决锁的可重入性问题

目的:允许持有锁的线程再次进入由同一锁保护的临界区,避免自身死锁。

说明:synchronized 是可重入的,而简单的 Lock 模拟往往不可重入。若线程在进入 outer() 方法持有锁后,内部又调用同样需要该锁的 inner(),非重入锁会导致当前线程阻塞。

动作:修改锁的实现逻辑,记录当前持有锁的线程 ID 以及重入次数。

public class MyReentrantLock {
  private boolean isLocked = false;
  private Thread lockedBy = null; // 记录谁持有了锁
  private int lockedCount = 0;    // 记录重入次数

  public synchronized void lock()
    throws InterruptedException {
    Thread callingThread = Thread.currentThread();
    // 如果锁未被占用,或被同一个线程再次请求,则允许进入
    while (isLocked && lockedBy != callingThread) {
      wait();
    }
    isLocked = true;
    lockedCount++;
    lockedBy = callingThread;
  }

  public synchronized void unlock() {
    if (Thread.currentThread() == this.lockedBy) {
      lockedCount--;
      if (lockedCount == 0) {
        isLocked = false;
        notify(); // 唤醒其他竞争锁的线程
      }
    }
  }
}

完成状态:当 lockedCount 降为 0 时,才算真正释放锁,此时 notify() 才会被触发。

第3步:正确处理异常以防死锁

目的:保证临界区抛出异常时,锁依然能被释放,从而避免其他所有尝试获取该锁的线程永久阻塞。

动作:使用 try...finally 结构包裹业务逻辑。

lock.lock();
try {
  // 执行可能抛出异常的临界区代码
} finally {
  lock.unlock();
}

注意事项:切勿仅在正常路径下调用 unlock(),否则异常导致的未解锁将使整个系统处于停滞状态。

第4步:优化策略选择

目的:根据场景需求平衡性能与公平性。

说明:默认的 Lock 实现通常不保证公平性(即不一定按等待时间排序分配锁),这可能导致部分线程饥饿。synchronized 也不保证公平性。如需严格的等待顺序,可使用 new ReentrantLock(true)(开启公平模式),但这会牺牲一定的吞吐量。

简要总结

在 Java 高并发场景中,合理使用 Lock 接口及其扩展功能(如可重入、公平性、超时控制)比直接使用 synchronized 提供了更高的灵活性。关键在于理解临界区的边界管理以及异常流下的资源回收。以上就是关于 java 多线程-锁详解及示例代码的详细内容,更多关于 java 并发编程的资料请关注本站其它相关文章!

本站声明

本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。

Python 相关产品和应用

全部下载

专题列表