出于好奇,当Java实现wait()和notify()方法时,他们真的只是使用锁吗?即,wait()获取互斥锁,notify()释放互斥锁,notifyAll()释放所有互斥锁(当然是在同一个对象中)?
除了比使用锁更简单之外,使用wait()和notify()还有其他优点吗?
[编辑]我意识到在Brian的评论之后我对此感到困惑:
等待没有锁定,它释放锁定并将其传递给正在等待互斥锁的同步语句的其他人,然后等待有锁定的其他人通知并通知锁定到调用wait的原始线程。我认为这就是你感到困惑的地方。 - Brian 17分钟前
答案 0 :(得分:12)
其他问题主要集中在wait
和notify
语言的含义上 - 但这似乎不是您的问题所在...您谈论的是互斥体,这是一个实现细节,因此特定于JVM。
所以我们需要选择一个JVM - 让我们选择openjdk(源可用here)。最终处理所有这些内容的代码位于hotspot/src/share/vm/runtime/objectMonitor.cpp
。
这维护了两个数据结构 - 等待集和条目集。等待线程被添加到等待集并停放,而尝试接收监视器的线程被添加到条目集然后停止。在notify
上,从等待集中获取一个线程并添加到条目集中。当一个线程释放锁时,如果有一个线程,它会从一个线程中取消一个线程。请注意,这些集实际上是作为队列(链表)实现的,因此将基于FIFO进行处理。
因此,在这种特殊情况下,实现会等待对象的监视器并尝试以类似的方式获取对象的监视器。
但这只是一个JVM的一个实现(虽然其他人可能会做类似的事情) - 所以我们不能依赖它。所以我想问题是你为什么想知道?如果仅仅是好奇心,那么请查看openjdk代码,这很有趣。如果您打算在代码中使用此信息,请不要这样做。
<强>更新强>
我意识到说&#34; park&#34;并没有告诉我们多少。停放线程的代码是特定于平台的(并且在名为PlatformEvent
的对象中实现,ParkEvent
扩展。在openjdk的版本中,我可以在hotspot/src/os/linux/vm/os_linux.cpp
找到Linux的公园代码,这会调用pthread_mutex_lock(_mutex)
...所以回答你的问题是调用wait 可能在我的计算机上使用互斥锁 。请注意,在此之上发生的很多事情可能会阻止我们达到这一点。
答案 1 :(得分:4)
wait()
和notify()
不进行任何监视器获取。作为这些方法的javadoc状态,调用者必须在调用之前获取监视器。事实上,wait()
实际上释放调用者获取的监视器(尽管我猜技术上等待 在最终返回之前监视(重新)获取)。
答案 2 :(得分:1)
wait
释放您已经拥有的锁定,以便在其他人调用notify
之后的某个时刻重新获取它。这是synchronized
提供的锁定机制的补充。基本上,您使用synchronized
获取锁定,然后使用wait
,notify
和notifyAll
来控制释放和重新锁定这些锁的方式。
答案 3 :(得分:1)
wait()
,notify()
始终与同步一起使用,因此可以在锁定和同步之间进行比较。像RentrantLock,ReadWriteLock和Synchronization [block,methods]这样的Locks有很大的不同。
锁定不在内部使用同步wait()
,notify()
需要同步。
你使用Lock和java.util.concurrent.locks.Condition,它有效地启用了条件锁定,这对于同步实现来说非常繁琐。
您没有任何同步的tryLock选项,您可以锁定或等待。但是使用Lock界面,您可以选择。
答案 4 :(得分:1)
出于好奇,当Java实现wait()和notify()方法时,他们真的只是使用锁吗? 即,wait()获取互斥锁,notify()释放互斥锁,notifyAll()释放所有互斥锁?
很抱歉,但这一切都没有。 :)
synchronized
关键字获取monitor
。即这是一种锁定形式。注意:使用java.util.concurrent.locks.Lock
做类似的事情。wait()
导致当前线程等待,直到另一个线程调用此对象的notify()
方法或notifyAll()
方法,或者已经过了指定的时间量。与锁定无关 - 但只能在当前线程当前线程已拥有此对象的监视器(在synchronized
块/方法中)时调用此方法notify()
唤醒正在等待此对象监视器的单个线程。再次,与锁定无关 - 但只能在当前线程当前线程已拥有此对象的监视器(在synchronized
块/方法中)时调用此方法