【问题标题】:Are Java wait(), notify() 's implementation significantly different from locks?Java wait(), notify() 的实现与锁有显着不同吗?
【发布时间】:2013-06-19 21:31:50
【问题描述】:

出于好奇,当 Java 实现 wait() 和 notify() 方法时,它们真的只是使用锁吗?即,wait() 获取一个互斥锁, notify() 释放一个互斥锁, notifyAll() 释放所有互斥锁(当然是在同一个对象中)?

除了比使用锁更方便之外,使用wait()和notify()还有其他好处吗?

[编辑] 在布赖恩的 cmets 之后,我意识到自己对什么感到困惑:

wait 不加锁,它释放锁并将其传递给正在等待互斥锁同步语句的其他人,然后等待拥有锁的其他人通知并调用 notify,将锁传回到调用等待的原始线程。我认为这就是你感到困惑的地方。 – 布赖恩 17 分钟前

【问题讨论】:

  • notifyAll() 释放同一对象上的所有等待。根本不是一回事。
  • @EJP 当然,我的意思是所有等待同一个对象,类似于释放该对象在 wait() 中获取的所有互斥锁,如果它们的隐式实现相同的话。
  • 等待和互斥锁怎么会是一回事?
  • 假设多个互斥体,这已经是不同的了。您假设存在差异,然后询问它们是否相同。不是一个真正的问题。
  • @EJP 也许我没有很好地解释我的问题。我做了一些编辑。你能再看一遍吗?

标签: java multithreading concurrency locking wait


【解决方案1】:

其他问题集中在语言所说的 waitnotify 是什么 - 但这似乎不是你的问题......你谈论的是互斥锁,这是一个实现细节和因此特定于 JVM。

所以我们需要选择一个 JVM——让我们选择 openjdk(源代码here)。在hotspot/src/share/vm/runtime/objectMonitor.cpp 找到(最终)处理所有这些东西的代码。

这维护了两个数据结构——一个等待集和一个入口集。等待线程被添加到等待集中并停放,而试图获取监视器的线程被添加到条目集中然后停放。在notify 上,从等待集中取出一个线程并将其添加到条目集中。当一个线程释放锁时,它会从条目集中释放一个线程(如果有的话)。请注意,这些集合实际上是作为队列(链表)实现的,因此按 FIFO 进行处理。

因此,在这种特殊情况下,实现将等待对象的监视器并尝试以类似的方式获取对象的监视器。

但这只是一个 JVM 的一种实现(尽管其他人可能会做类似的事情)——所以我们不能依赖它。所以我想问题是你为什么想知道?如果只是好奇那就翻开openjdk的代码,很有意思。如果你打算在你的代码中使用这些信息......不要。

更新

我意识到说“公园”并不能告诉我们太多。停放线程的代码是特定于平台的(并在名为PlatformEvent 的对象中实现,ParkEvent 扩展了该对象)。在我正在查看 linux 的 park 代码的 openjdk 版本中,可以在 hotspot/src/os/linux/vm/os_linux.cpp 找到这个调用 pthread_mutex_lock(_mutex)... 所以回答你的问题 调用 wait 可以在我的机器上使用一个互斥锁。请注意,在此之上会发生很多事情,这可能会阻止我们到达这一点。

【讨论】:

  • objectMonitor.cpp 中的实现看起来类似于here 中为wait() 显示的合约。进一步讨论,here
【解决方案2】:

wait()notify() 不进行任何监视器采集。正如这些方法的 javadoc 所述,调用者必须在调用之前获取监视器。事实上,wait() 实际上释放调用者获取的监视器(虽然,我想技术上等待确实在最终返回之前监视(重新)获取)。

【讨论】:

  • 那么“监视器”和锁有什么不同呢?
  • 这不是真的,这只是 javadoc 使用的术语。
  • 我猜监视器和锁之间有很大的不同。 Monitor 是一个观察者,它检查想要执行被监控代码的进程是否获得了正确的锁。 javaworld.com/article/2076971/java-concurrency/…
  • @3yakuya 不是真的...监视器是 Java 用于维护互斥锁状态的操作系统提供的低级结构的名称。该结构与 Java 对象分离,并且只为那些监视器实际上被多个线程争用的对象延迟分配。监视器不是活动进程。
  • 当你说实际释放监视器。您对监视器的解释是什么意思? class ObjectMonitor 来自 /src/share/vm/runtime/objectMonitor.hpp? -1 来自我
【解决方案3】:

wait 释放您已经拥有的锁,以便在其他人调用notify 后的某个时间点重新获得它。这是对synchronized 提供的锁定机制的补充。基本上,您使用synchronized 获取锁,然后使用waitnotifynotifyAll 来控制这些锁的释放和重新锁定方式。

【讨论】:

    【解决方案4】:

    出于好奇,当 Java 实现 wait() 和 notify() 方法时,它们真的只是使用锁吗? 即,wait() 获取互斥锁, notify() 释放互斥锁, notifyAll() 释放所有互斥锁?

    抱歉,这些都不对。 :)

    • 获取monitor的是synchronized关键字。即这是一种锁定形式。注意:使用 java.util.concurrent.locks.Lock 会做类似的事情。
    • wait() 导致当前线程等待,直到另一个线程为此对象调用 notify() 方法或 notifyAll() 方法,或者经过了指定的时间量。与锁定无关 - 但只能在当前线程当前线程已经拥有此对象的监视器时调用此方法(在 synchronized 块/方法中)
    • notify() 唤醒正在此对象的监视器上等待的单个线程。同样,与锁定无关 - 但只能在当前线程当前线程已经拥有此对象的监视器时调用此方法(在 synchronized 块/方法中)

    【讨论】:

    • 我对我原来的问题做了一些修改。也许这使我的问题更清楚。你能看看吗?从你的回答来看,如果我理解正确的话,锁的实现和wait()、notify()的实现没有本质的区别
    【解决方案5】:

    wait()notify() 总是与 Synchronization 一起工作,因此可以在 Locks 和同步之间进行比较。 RentrantLock、ReadWriteLock 和 Synchronization [block, methods] 等锁之间存在很大差异。

    1. 锁在内部不使用同步,而wait()notify() 将需要同步。

    2. 您将 Lock 与 java.util.concurrent.locks.Condition 一起使用,这可以有效地启用条件锁定,这与同步实现起来相当繁琐..

    3. 您没有任何带有同步的 tryLock 选项,您要么锁定,要么等待。但是使用 Lock 界面,您可以选择。

    【讨论】:

    • 我的意思是使用 java.util.concurrent.locks.Condition,这使得条件锁的实现更高效、更容易。我将编辑我的答案。
    • 条件相当于wait/notify,所以还是没看懂2.
    • @jtahlborn:它易于实施。因此,如果要调用 wait(),您必须处于同步块中,但我可以根据我的逻辑在一部分代码中调用 lock 并在另一部分中等待。它可以帮助我更好地组织我的代码并允许更精细的实现。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-11-19
    • 2016-03-21
    • 1970-01-01
    相关资源
    最近更新 更多