【问题标题】:Reentrant lock implementation detail可重入锁实现细节
【发布时间】:2021-05-01 19:36:04
【问题描述】:

我试图了解ReentrantLock::lock 方法中的特定细节。我正在查看它并将其视为:

final void lock() {
   if (!initialTryLock()) {
       acquire(1);
   }
}

所以它首先尝试这种方法:initialTryLock(我会查看NonfairSync),它会这样做:

  • 它执行compareAndSwap(0, 1),这意味着如果没有人持有锁 (0) 而我可以抓住它 (1),我现在就持有锁。
  • 如果上述失败,它会检查请求锁的线程是否已经是所有者。
  • 如果失败,则返回false,这意味着我无法获取锁。

让我们假设上述失败。然后它继续并在AbstractQueuedSynchronizer 中调用acquire

public final void acquire(int arg) {
    if (!tryAcquire(arg))
        acquire(null, arg, false, false, false, 0L);
}

它首先在NonfairSync中调用tryAcquire

protected final boolean tryAcquire(int acquires) {
    if (getState() == 0 && compareAndSetState(0, acquires)) {
        setExclusiveOwnerThread(Thread.currentThread());
        return true;
    }
    return false;
}

您可以看到它尝试再次获取锁,尽管initialTryLock 已经失败。理论上,这个tryAcquire 可以简单地返回false,对吧?

我认为这是一次潜在的重试,因为在initialTryLocktryAcquire 的调用之间,锁可能已被释放。这样做的好处可能是因为下一个操作(在tryAcquire 之后)失败,是该线程的昂贵入队。所以我想这是有道理的(重试)因为那个?

【问题讨论】:

  • 你说的是哪个版本的java src?
  • @Andrea 我的错,我在看 jdk-15;如果这很重要。
  • 在java 12 ReentrantLock::lock 直接调用sync.acquire(1) 并且函数initialTryLock 不存在。所以你提到的代码是最近介绍的。如果有人要调查,他应该参考正确的java版本
  • @Andrea 好点。我添加了正确的标签。谢谢

标签: java multithreading concurrency reentrantlock java-15


【解决方案1】:

只需添加到the answer above

tryAcquire 可以简单地返回 false,对吧?

没有。

这个实现:

boolean tryAcquire(int acquires) {
  return false;
}

会破坏AbstractQueuedSynchronizer的工作。

原因是tryAcquire() 是在AbstractQueuedSynchronizer 中获取锁的唯一方法。

甚至acquire() in the end uses tryAcquire()

所以如果tryAcquire() 总是返回false 那么acquire() 将永远不会获得锁。

acquire()用于多个线程争夺锁的时候。

【讨论】:

  • 我在acquire 中看到了tryAcquire 的用法,但据我所知,here 它完全可以失败,即:acquired 变为false。最初,这让我有点失望。现在我查看了您提到的代码,如果tryAcquire 将返回false,那么AbstractQueueSynchronizer 的任何扩展类都无法获得ExclusiveNode 的锁,确实如此。谢谢,现在一切都说得通了。
【解决方案2】:
  • initialTryLock() 包含重入功能:

    • javadoc:
      /**
      * Checks for reentrancy and acquires if lock immediately
      * available under fair vs nonfair rules. Locking methods
      * perform initialTryLock check before relaying to
      * corresponding AQS acquire methods.
      */
      abstract boolean initialTryLock();
      
    • NonfairSync中的源代码:
      final boolean initialTryLock() {
          Thread current = Thread.currentThread();
          if (compareAndSetState(0, 1)) { // first attempt is unguarded
              setExclusiveOwnerThread(current);
              return true;
          } else if (getExclusiveOwnerThread() == current) {
              int c = getState() + 1;
              if (c < 0) // overflow
                  throw new Error("Maximum lock count exceeded");
              setState(c);
              return true;
          } else
              return false;
      }
      
      这里:
      • 第一个if 检查锁是否被占用(如果它是空闲的则占用锁)
      • 第二个if 检查获取的锁是否属于当前线程——这是重入逻辑。
  • tryAcquire()必须由任何扩展AbstractQueuedSynchronizer的类实现

    • 该方法必须执行的操作在其 javadoc 中的 AbstractQueuedSynchronizer 中进行了描述
      /**
       * Attempts to acquire in exclusive mode. This method should query
       * if the state of the object permits it to be acquired in the
       * exclusive mode, and if so to acquire it.
       * ...
       */
      protected boolean tryAcquire(int arg) {
          throw new UnsupportedOperationException();
      }
    
    • NonfairSync 中的实现正是这样做的(并且不包含重入功能):
    /**
    * Acquire for non-reentrant cases after initialTryLock prescreen
    */
    protected final boolean tryAcquire(int acquires) {
        if (getState() == 0 && compareAndSetState(0, acquires)) {
            setExclusiveOwnerThread(Thread.currentThread());
            return true;
        }
        return false;
    }
    

【讨论】:

  • 那么是什么阻止了NonfairSync::tryAcquire 返回false?它是一个具有非公共方法的非公共类。
  • 来自user15110003tryAcquire()AbstractQueuedSynchronizer内部的几个地方使用。在您发现的地方,如果 tryAcquire() 什么都不做并返回 false 会很好。但是在AbstractQueuedSynchronizer 内部的其他地方,如果tryAcquire() 没有遵守其在javadoc 中为AbstractQueuedSynchronizer::tryAcquire 的合同,它会破坏AbstractQueuedSynchronizer
  • @dan1st 对。我希望我错过了它,但是你能发现tryAcquire 后面没有acquireAbstractQueuedSynchronizer 中的任何代码流吗?我确实理解遵守 javadoc,但返回 false 不会违反这一点。我的意思是默认实现甚至会引发异常。除了 javadoc 说:尝试以独占模式获取initialTryLock 已经通过if (compareAndSetState(0, 1) 完成了。顺便说一句,您在发表评论时必须用@ 标记我
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2016-10-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多