Lazy loaded image
🗒️ReentrantLock jdk8 源码分析
Words 1248Read Time 4 min
2025-10-11
2025-11-13
type
status
date
slug
summary
tags
category
icon
password
原文
ReentrantLock 是 Java 并发包(java.util.concurrent.locks)中提供的 可重入独占锁,实现了 Lock 接口,是 synchronized 关键字的增强替代方案,具备更灵活的锁控制能力。其核心特点是「可重入性」和「可定制的公平性」,同时支持中断响应、超时获取锁、条件变量等高级功能。
ReentrantLock 基于 AQS 来实现的独占锁。

基本特性

  • 可重入性:
    • 借助 AQS 的 state 变量做为计数器来实现,首次获取锁时,state 重置为 1,重入时state++,释放时 state— ,直到 state为 0 才真正释放锁。
      page icon
      有 lock 操作,就必须有 unlock 操作。
      如果只有 lock,忘记 unlock,锁没办法释放的,其他加入到队列中等待唤醒节点就会一直处于 WAITING 状态。
      如果只有 unlock,没有 lock,是会抛出异常的,因为 state必须为 0,才表示锁是真正释放的。
  • 公平性:支持公平锁,非公平锁。通过构造器指定一个布尔参数,默认为非公平锁。
  • 可中断。
    • 支持「可中断的锁获取」:线程在等待锁的过程中可以被中断(调用 lockInterruptibly()),无需一直阻塞,适合需要灵活退出等待的场景。(synchronized 不支持中断,等待的线程只能一直阻塞或直到获取锁。)
  • 超时获取锁
    • 可以通过 tryLock(long timeout, TimeUnit unit) 尝试在指定时间内获取锁,超时未获取则返回 false,避免无限期阻塞,适合需要控制等待时长的场景。

运行流程

lock流程

图1 lock 操作
图1 lock 操作
多个线程使用CAS方式抢占同一把锁,假设Thread2 抢占到了,那么会将ReentrantLock对象中的 exclusiveOwnerThread 设置为CAS 成功的的线程,且将线程计数器+1(state),其他未能抢占到锁的线程会加入到一个双向链表构成的队列,且会调用线程的interrupt 方法,线程进入 WAITING 状态。
page icon
调用线程的sleep方法来模拟线程工作,在使用IDE(IntelliJ IDEA )工具进行 Debug时,在 DEBUG 工具栏,线程的状态显示的 SLEEPING 状态,但是使用arthas工具来观察线程时显示的是 TIMED_WAIT,在 Thread 源码中是没有 SLEEPING状态的。

公平锁和非公平锁在加锁流程区别

ReentrantLock 内部有个静态内部类Sync继承了 AQS,同时内部FairSyncNonfairSync 两个内部类继承自 Sync,在初始化ReentrantLock 时来指定 sync 使用公平锁还是非公平锁。
  • 非公平锁:直接CAS操作是否能将exclusiveOwnerThread设置为当前线程
    • 公平锁:获取锁时先检查等待队列中是否有前驱节点有等待获取锁的线程(hasQueuedPredecessors()),若有则必须入队等待,不允许抢占。

      队列

      • 未能抢占锁的线程会进入队列。
      • 队列是由一个双向链表构成的。
      • 抢占锁的时候,即当前线程调用 lock 方法时,如果是公平锁且在锁未被持有的情况下会判断当前线程是否有前驱节(tail.next 是否是当前线程)。

      unlock流程

      解锁的本质是将当前线程的计数器(state)重置为 0,然后exclusiveOwnerThread 重置为 null。
      • 加锁和解锁必须是同一线程
      • 释放锁后,唤醒队列中后续节点。
       
      上一篇
      CountDownLatch jdk8 源码分析
      下一篇
      StampedLock jdk8 源码分析

      Comments
      Loading...