首先,这是一个近乎重复的: How to differentiate when wait(long timeout) exit for notify or timeout?
但这是一个新的后续问题。
有这个等待声明:
public final native void wait(long timeout) throws InterruptedException;
它可能会被InterruptedException或超时退出,或者因为Notify / NotifyAll方法在另一个线程中被调用,所以Exception很容易被捕获但是......
我的代码绝对需要知道退出是来自超时还是通知。 (将来,这段代码需要重新设计,但现在无法完成。所以我需要知道退出等待的原因。)
具体来说,有人可以使用一个示例使用ThreadLocal布尔值,该布尔值仅在notify()上设置为true,并且所有这些都在现有循环中,如下所示? (这或多或少是另一个线程中接受的答案,但没有给出具体的代码示例。我不熟悉Java,所以我需要一个特定的代码示例 - 理想情况下在下面的现有代码的上下文中。)
public synchronized int getLastSequenceNumber() {
while (empty) {
try {
wait(waitTimeValue);
} catch (InterruptedException e) {}
}
empty = true;
return reportedSequenceNumber;
}
public synchronized void reconcileLastSequenceNumber(int sequenceNumber) {
empty = false;
this.reportedSequenceNumber = sequenceNumber;
notifyAll();
}
布尔“空”用于我在此问的具体问题之外的目的。我相信我需要添加另一个布尔值来满足原始问题的建议答案。如何将提议的解决方案集成到上面的现有代码片段中?感谢。
答案 0 :(得分:8)
答案 1 :(得分:3)
你应该使用当前的循环,无论是否知道wait
是否超时 - 部分是由于虚假唤醒的可能性。但是,我完全不确定真的需要知道是否因为通知而退出了呼叫。
考虑通知在超时之前纳秒发生的情况与超时后纳秒发生通知的情况。这两者之间有用的区别是什么?从根本上说,如果两者发生在“大致相同的时间”,就会出现竞争条件。
据我所知,wait()
确实没有让您判断通话是否超时,但不应影响你的代码。您应该循环并测试其他通知副作用的其他内容。
我不清楚ThreadLocal
在哪个方面会发挥作用 - 如果你需要能够从等待的线程中分辨出来,这就是你想要的相反是否通知线程已达到某一点。我认为你根本不需要额外的变量 - 你的empty
很好。
答案 2 :(得分:1)
使用内置监视器API无法直接报告此问题,但您可以使用新的实现替换wait()
和其他函数,以明确跟踪此(未经测试):
private int wait_ct = 0, signal_ct = 0;
public void checkedNotifyAll() {
synchronized {
signal_ct = wait_ct;
notifyAll();
}
}
public void checkedNotify() {
synchronized {
signal_ct++;
if (signal_ct > wait_ct)
signal_ct = wait_ct;
notify();
}
// Returns true if awoken via notify
public boolean waitChecked(long timeout, int nanos) throws InterruptedException {
synchronized(this) {
try {
wait_ct++;
super.wait(timeout, nanos);
if (signal_ct > 0) {
signal_ct--;
return true;
}
return false;
} finally {
wait_ct--;
if (signal_ct > wait_ct) signal_ct = wait_ct;
notify(); // in case we picked up the notify but also were interrupted
}
}
// Note: Do not combine this with normal wait()s and notify()s; if they pick up the signal themselves
// the signal_ct will remain signalled even though the checkedWait()s haven't been
// awoken, potentially resulting in incorrect results in the event of a spurious wakeup
当然,这不一定是做到这一点的好方法;如果在调用notify()
之前超时,则信号状况可能会丢失。你真的应该在循环中等待,检查一些持久条件。
答案 3 :(得分:1)
这是基于Jenkov信号类的扩展版本。如果它不以Notify
结尾,则会引发异常。当我遇到同样的问题时,认为这可能会有所帮助。
public class MonitorObject{
}
public class Signal{
MonitorObject myMonitorObject = new MonitorObject();
boolean wasSignalled = false;
public void doWait(int timeOut) throws InterruptedException,TimeoutException{
synchronized(myMonitorObject){
long startTime = System.currentTimeMillis();
long endTime = startTime + timeOut;
Log.d(TAG, String.format("MonitorStart time %d",startTime));
while(!wasSignalled){
long waitTime = endTime - System.currentTimeMillis();
if(waitTime > 0)
myMonitorObject.wait(waitTime);
else{
Log.e(TAG, String.format("Monitor Exit timeout error"));
throw new TimeoutException();
}
}
Log.d(TAG, String.format("MonitorLoop Exit currentTime=%d EndTime=%d",System.currentTimeMillis(),startTime + timeOut));
//Spurious signal so clear signal and continue running.
wasSignalled = false;
}
}
public void doNotify(){
synchronized(myMonitorObject){
wasSignalled = true;
myMonitorObject.notify();
}
}
}