据一位同事介绍,JVM 不保证当对一个对象调用“notify”时,那个时候会通知正确的“wait”。他说,可能存在一种情况,即先前不再有效的通知在无效时间交付。
这是真的?如果是这样,这是如何/为什么会这样,如果你不能假设这样基本的东西会起作用,等待/通知机制有什么用?
据一位同事介绍,JVM 不保证当对一个对象调用“notify”时,那个时候会通知正确的“wait”。他说,可能存在一种情况,即先前不再有效的通知在无效时间交付。
这是真的?如果是这样,这是如何/为什么会这样,如果你不能假设这样基本的东西会起作用,等待/通知机制有什么用?
对于 java.lang.Object.notify,Javadoc 说:
唤醒正在此对象的监视器上等待的单个线程。如果有任何线程正在等待该对象,则选择其中一个被唤醒。该选择是任意的,并由实施自行决定。线程通过调用其中一个等待方法在对象的监视器上等待。
这是等待特定条件的模式:
synchronized( lock ) {
while( conditionEvaluation( data )) {
lock.wait();
}
}
对方应使用java.lang.Object.notifyAll()
以确保应用程序的活力。即使今天只有一个服务员,经过软件的多次演进,未来可能是几个服务员,所以notifyAll()
比notify()
.
每个等待内在锁的对象都将进入锁的等待集。当您在锁对象上调用 notify 时,将选择其等待集中的线程之一来恢复工作。JVM 提供的唯一保证是等待的线程最终将通知。这种非确定性行为的主要原因之一是 JVM 选择挂起线程运行的方式,这是任意的。然而,此外,java 中的锁实现了允许线程插入的非公平锁定策略。这仅仅意味着允许新的锁请求跳到锁的等待集之前,如果锁在请求时可用。这背后的理由是,鉴于存在大量争用,在选择和恢复等待集中的挂起线程及其实际运行时间之前,可能会有一些(潜在的重大)延迟。因此,来自线程的任何传入锁请求都可以利用此时间延迟立即运行,希望在恢复的线程准备好运行时它已经释放了锁。
很明显,在第 2 步和第 6 步之间存在一些时间间隔,其中没有线程实际使用锁。线程 C 插入并利用时间间隔作为优化。这样做的缺点当然是在线程 B 准备运行时没有释放锁的风险,此时线程 B 会注意到锁不可用并再次进入等待集。然而,从统计上可以证明,非公平锁定在大多数情况下提供了更好的性能。
顺便说一句,您可以使用公平锁,等待线程按照它们获取锁的顺序恢复,但实际上这会提供更差的性能。在此处阅读有关此内容的更多信息:http: //docs.oracle.com/javase/1.5.0/docs/api/java/util/concurrent/locks/ReentrantLock.html
我希望这回答了你的问题。
不,这不是真的。当一个线程调用 notify 时,会唤醒一个等待线程(如果存在这样的线程,则通知丢失)。可能您的同事想到了“虚假通知”,它可以在实际上没有其他线程调用时唤醒一个线程notify
或notifyAll
. 为了过滤“虚假通知”,每次notify
调用都应伴随受监视对象状态的一些变化,等待线程应检查该状态:
synchronized void up() {
counter++;
notify();
}
synchronized void down() {
while (counter==0) {
wait();
}
counter--;
}
注意检查状态down()
是在调用 wait() 之前完成的,因为它可以在调用之前更改并且通知丢失。换句话说,真正的信息与对象的状态一起传递,等待/通知仅有助于避免轮询。永远不要在不改变对象状态的情况下依赖通知。