Java 监视器:如何知道 wait(long timeout) 是由超时还是由 Notify() 结束?

发布于 2024-11-09 14:50:03 字数 1180 浏览 3 评论 0原文

首先,这几乎是以下内容的重复: 如何区分 wait(long timeout) 退出时通知或超时?

但这是一个新的后续问题。

具有此等待声明:

public final native void wait(long timeout) throws InterruptedException;

它可以通过 InterruptedException 退出,或者通过超时退出,或者因为在另一个线程中调用 Notify/NotifyAll 方法,异常很容易捕获,但是......

我的代码绝对需要知道退出是由于超时还是通知。 (将来,这段代码需要重新设计,但现在不能这样做。所以我需要知道退出等待的原因。)

具体来说,有人可以给出一个使用a的例子 ThreadLocal 布尔值仅在 notification() 上设置为 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();
}

布尔值“空”的用途超出了我在这里提出的具体问题。我相信我需要添加另一个布尔值来满足原始问题的建议答案。我如何将该建议的解决方案集成到上面现有的代码片段中?谢谢。

First, this is a near duplicate of:
How to differentiate when wait(long timeout) exit for notify or timeout?

But it is a new follow-on question.

Having this wait declaration:

public final native void wait(long timeout) throws InterruptedException;

It could exit by InterruptedException, or by timeout, or because Notify/NotifyAll method was called in another thread, Exception is easy to catch but...

My code absolutely needs to know if the exit was from timeout or notify. (In the future, this code needs to be redesigned, but that cannot be done now. So I need to know the reason for the exit from wait.)

Specifically, can someone give an example of using a ThreadLocal Boolean that is set to true only on notify() and where all this is inside an existing loop as shown below? (This was more or less the accepted answer in the other thread, but no specific code example was given. I'm not all that familiar with Java, so I need a specific code example -- ideally in the context of the existing code below.)

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();
}

the Boolean "empty" serves a purpose outside of the specific question I'm asking here. I believe I will need to add another Boolean to fulfill the suggested answer from the original question. How would I integrate that proposed solution into the existing code snippet above? Thanks.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

聆听风音 2024-11-16 14:50:03

您可能最好使用 条件(及其await 方法)而不是内置监视器,因为 await 返回一个 boolean 值,指示等待是否计时出去。

即使如此,您也必须提防虚假唤醒(这与对 信号。)

You might be better off using a Condition (and its await method) rather than built-in monitors, because await returns a boolean value indicating whether the wait timed out.

And even then, you must beware of spurious wakeup (which is indistinguishable from a call to signal.)

2024-11-16 14:50:03

无论如何,您应该像当前一样使用循环,无论是否知道 wait 是否超时 - 部分原因是可能出现虚假唤醒。但是,我完全不确定您是否真的需要知道呼叫是否由于通知而退出。

考虑通知在超时前一纳秒发生的情况与通知在超时后一纳秒发生的情况。两者之间有什么有用区别?从根本上讲,如果两者“大约同时”发生,则存在竞争条件。

据我所知,wait()确实不会让您判断调用是否超时,但它不应该 > 影响你的代码。无论如何,您应该循环并测试其他作为通知副作用的东西。

老实说,我不清楚 ThreadLocal 会在哪里发挥作用 - 如果您需要能够从等待线程中分辨出来,这恰恰与您想要的相反通知线程是否已达到某个点。我认为您根本不需要额外的变量 - 您的 empty 就可以了。

You should be using a loop as you currently are anyway, regardless of knowing whether the wait timed out - partly due to the possibility of spurious wakeups. However, I'm not at all sure that you really need to know whether the call exited due to notification or not.

Consider the situation where the notification occurs a nanosecond before the timeout vs the situation where the notification occurs a nanosecond after the timeout. What's the useful difference between the two? Fundamentally there's a race condition if the two occur at "about the same time".

As far as I can tell, wait() really doesn't let you tell whether the call timed out or not, but it shouldn't affect your code. You should be looping and testing something else that is a side-effect of the notification anyway.

It's not clear to me where a ThreadLocal would come into play to be honest - that's exactly the opposite of what you want if you need to be able to tell from the waiting thread whether the notifying the thread has reached a certain point. I don't think you need an extra variable at all - your empty is fine.

街道布景 2024-11-16 14:50:03

没有直接的方法可以使用内置监视器 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() 之前超时,信号条件可能会丢失。您确实应该在循环中等待,检查一些持久条件。

There's no direct way to report this with the builtin monitor API, but you could replace the wait() and other functions with a new implementation that tracks this explicitly (untested):

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

This isn't necessarily a good way to do this, of course; if you timeout just before notify() is called, the signal condition may be lost, after all. You really should be waiting in a loop, checking some persistent condition.

标点 2024-11-16 14:50:03

这是基于 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();
         }
     }
 } 

This is an expanded version based on Jenkov's signal class. An exception is raised if it does not end with a Notify. Thought it might help as I ran into the same problem.

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();
         }
     }
 } 
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文