不幸的是,由于周围代码的复杂性太高,我无法完全理解这一点。缺点是:
我有一段等待锁定的代码:
synchronized (lock) {
lock.wait();
}
哪个按预期工作。相当简单 - 它获取锁,在它开始等待时释放它,另一个线程获取锁,然后通知它。
但是,只要我提供超时,行为就会完全改变。
synchronized (lock) {
lock.wait(60000L);
}
同样,应该相当简单(这在代码中的其他几个地方也可以正常工作)。但是,在这种情况下,执行基本上会停止,直到超时发生。关于似乎正在发生的事情的唯一猜测是它在进入等待时没有释放锁定 - 通知程序永远无法获取锁定,因此等待会一直睡到它超时。更糟糕的是,它是一个阻塞睡眠 - 没有其他线程可以等待锁定,它会强制执行完全同步。
任何人都对这里可能发生的事情有任何想法?它是一个相当简单的功能,并且在任何时候都没有任何奇怪的嵌套同步块。考虑到通过不提供超时,它应该无限期地等待,如果通知器本身被破坏,代码将永远挂起,但事实并非如此。只有在提供超时后它才会停止工作。
任何想法都将不胜感激。
操作系统:OS X 10.8.5
JDK:1.6.0,1.7.0.45和1.7.0.67
答案 0 :(得分:1)
您的示例未在wait()调用周围显示while()循环。这表明您可能无法完全理解等待和通知的用例。这是一个例子:
// This object is used to synchronize *EVERY* method
// that can change the value of count.
final Object lock = new Object();
int count;
void waiter() {
synchronized(lock) {
while(count <= 0) {
lock.wait();
}
//do something that you are only allowed to do
//when count > 0.
}
}
void notifier() {
synchronized(lock) {
count++;
if (count >= 0) {
lock.notify();
}
}
}
[编辑:添加了这一段,感谢Nathan Hughes提醒我......] wait()调用处于循环中,因为wait()ing线程仍然需要重新获取锁定已通知锁:如果线程A正在等待条件变为真,并且线程B使条件为真并调用notify();我不能保证线程C不会先获得锁定,并且在wait()调用能够返回之前再次使条件为false。
此外,即使没有通知对象(这被称为&#34;虚假唤醒&#34;),也允许wait()返回。
要等待的条件在代码中是明确的(即,count> 0)。
除非在用于wait()和notify()调用的同一个锁对象上进行同步时,否则不会更改要等待的条件。
答案 1 :(得分:0)
无论是否提供超时,对象上的wait方法都会释放由当前线程保存在对象上的锁,如John所评论的那样。
根据您给出的代码并根据您对场景的描述,我的猜测是执行lock.wait(60000L)的时刻JVM释放对象的锁定,同时处于runnable / running状态的任何其他线程可能如果他们正在同一个对象上进行同步,那么他们可能会在你的通知程序线程获取锁之前获取锁定。
这种行为很难调试,因为它依赖于JVM分析器来选择应该运行的线程。因此,正如您在执行lock.wait(60000L)时所解释的那样,并非总是需要通知程序线程才能获取对公共对象的锁定。如果还有任何其他线程也在等待公共对象,它很可能最终导致通知程序线程无法获得锁定,因此lock.wait(60000L)会超时。
答案 2 :(得分:-1)
每当您使用 lock.wait(..)时,您必须使用 lock.notify()或 lock.notifyAll() 。确保在逻辑中使用它有意义,并且在超时之前“唤醒”锁(考虑到你输出的超时值就足够了)。这是它的一些使用指南,我希望它有用:http://www.javamex.com/tutorials/wait_notify_how_to.shtml