Java ReentrantLock 冗余设计?

HowieWang · 2025-3-10 12:33:59 · 312 次点击

背景

偶然看到了一篇 ReentrantLock 的源码分析文章,自己便去学习了一下源码。 核心思想是如果被请求的共享资源空闲,那么就将当前请求资源的线程设置为有效的工作线程,将共享资源设置为锁定状态;如果共享资源被占用,就需要一定的阻塞等待唤醒机制来保证锁分配。

问题

ReentrantLock 的 lock 方法在入队之前直接获取锁,是否冗余设计,存在冗余的代码执行?

我的观点

进行了冗余设计,理解如下。

jdk8

lock 简化代码如下,我的观点是:非公平锁(NofairSync) lock 方法,有两次尝试直接获取锁,是否冗余设计了?

static final class NonfairSync extends Sync {
  final void lock() {
    // 直接尝试获取锁,不公平表现
    if (compareAndSetState(0, 1))
        setExclusiveOwnerThread(Thread.currentThread());
    else
        acquire(1); // acquire 方法中首先会调用 tryAcquire ,会第二次尝试获取锁
  }

  protected final boolean tryAcquire(int acquires) {
    // 如果资源空闲,直接尝试获取锁
    // 如果资源不空闲,判断是否当前线程占有,进行重入锁
  }
}

static final class FairSync extends Sync {
  final void lock() {
      acquire(1);
  }

  protected final boolean tryAcquire(int acquires) {
    // 如果资源空闲,判断是否已经入队,进行公平处理
    // 如果资源不空闲,判断是否当前线程占有,进行重入锁
  }
}

class AbstractQueuedSynchronizer {
  public final void acquire(int arg) {
    if (!tryAcquire(arg) &&
        acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
        selfInterrupt();
  }
}

jdk17

lock 简化代码如下,我的观点是:lock 方法,有两次尝试直接获取锁,是否冗余设计了?对于公平锁,均会判断当前线程是否已经入队,是冗余的尝试;对于非公平锁,连续两次尝试直接获取锁,也是冗余的尝试

abstract static class Sync extends AbstractQueuedSynchronizer {

    abstract boolean initialTryLock();

    final void lock() {
        if (!initialTryLock()) // 首次尝试获取锁
            acquire(1); // acquire 方法中首先会调用 tryAcquire ,会第二次尝试获取锁
    }
}

static final class NonfairSync extends Sync {
  final boolean initialTryLock() {
    // 如果资源空闲,直接尝试获取锁
    // 如果资源不空闲,判断是否当前线程占有,进行重入锁
  }

  protected final boolean tryAcquire(int acquires) {
    // 如果资源空闲,直接尝试获取锁
  }
}

static final class FairSync extends Sync {
  final boolean initialTryLock() {
    // 如果资源空闲,判断是否已经入队,进行公平处理
    // 如果资源不空闲,判断是否当前线程占有,进行重入锁
  }

  protected final boolean tryAcquire(int acquires) {
    // 如果资源空闲,判断是否已经入队,进行公平处理
  }
}

我认为的不冗余的方式

static class Sync extends AbstractQueuedSynchronizer {
  final void lock() {
    acquire(1);
  }
}

static final class NonfairSync extends Sync {

  protected final boolean tryAcquire(int acquires) {
    // 如果资源空闲,直接尝试获取锁
    // 如果资源不空闲,判断是否当前线程占有,进行重入锁
  }
}

static final class FairSync extends Sync {

  protected final boolean tryAcquire(int acquires) {
    // 如果资源空闲,判断是否已经入队,进行公平处理
    // 如果资源不空闲,判断是否当前线程占有,进行重入锁
  }
}
举报· 312 次点击
登录 注册 站外分享
2 条回复  
herm2s 初学 2025-3-10 14:52:02
不算设计冗余,两次调用 compareAndSetState 都是为了在无竞争时快速成功。第一次调用返回 false 意味着有竞争,此时进入 acquire 。在 acquire 里会根据 state 的值判断是否调用 compareAndSetState ,原因是这中间有可能从有竞争变为无竞争( volatile ),此时快速成功就行了。state 的值>0 才是可重入锁/其它线程占用的情况,最后再做其它处理。
Rickkkkkkk 初学 2025-3-10 14:55:23
你的疑问算是一个面试题了
返回顶部