Мониторы Java: как узнать, завершилось ли ожидание (длительное время ожидания) тайм-аутом или функцией Notify ()? - PullRequest
6 голосов
/ 24 мая 2011

Во-первых, это почти копия: Как различить время ожидания (длинный тайм-аут) для уведомления или тайм-аута?

Но это новый дополнительный вопрос.

Имея это объявление ожидания:

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

Он может завершиться по InterruptedException, или по таймауту, или потому что метод Notify / NotifyAll был вызван в другом потоке, Exception легко поймать, но ...

Мой код должен знать, был ли выход из таймаута или уведомлять. (В будущем этот код должен быть переработан, но этого нельзя сделать сейчас. Поэтому мне нужно знать причину выхода из ожидания.)

В частности, может ли кто-нибудь привести пример использования логического значения ThreadLocal, для которого установлено значение true только для notify (), и где все это находится внутри существующего цикла, как показано ниже? (Это был более или менее принятый ответ в другом потоке, но не был приведен конкретный пример кода. Я не так уж хорошо знаком с 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();
}

логическое «пустое» служит цели вне конкретного вопроса, который я здесь задаю. Я считаю, что мне нужно будет добавить еще один логический тип, чтобы выполнить предложенный ответ из исходного вопроса. Как бы я интегрировал это предлагаемое решение в существующий фрагмент кода выше? Спасибо.

Ответы [ 4 ]

8 голосов
/ 24 мая 2011

Возможно, лучше использовать Condition (и его метод await), а не встроенные мониторы, потому что await возвращает значение booleanуказывая, истекло ли время ожидания.

И даже в этом случае вы должны остерегаться ложного пробуждения (которое неотличимо от вызова на signal.)

3 голосов
/ 24 мая 2011

В любом случае вы должны использовать цикл, которым вы сейчас пользуетесь, независимо от того, истек ли срок ожидания wait - отчасти из-за вероятности ложных пробуждений.Тем не менее, я совсем не уверен, что вам действительно нужно знать, вышел ли вызов из-за уведомления или нет.

Рассмотрим ситуацию, когда уведомление происходит наносекунду до истечения времени ожидания противситуация, когда уведомление происходит наносекунда после тайм-аута.В чем полезная разница между ними?По сути, существует состояние гонки, если эти два события происходят «примерно в одно и то же время».

Насколько я могу судить, wait() действительно не позволяет вам сказать, был ли вызов синхронизированили нет, но это не должно влиять на ваш код.В любом случае, вы должны зацикливаться и тестировать что-то еще, что является побочным эффектом уведомления.

Мне не совсем понятно, где в игру вступит ThreadLocal, если честно - это как раз и есть того, что вы хотите, если вам нужно быть в состоянии узнать из ожидающего потока, достигла ли уведомляющая нить определенная точка.Я не думаю, что вам нужна дополнительная переменная - ваш empty в порядке.

1 голос
/ 05 февраля 2014

Это расширенная версия, основанная на классе сигналов Дженкова. Возникает исключение, если оно не заканчивается на 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();
         }
     }
 } 
1 голос
/ 24 мая 2011

Нет прямого способа сообщить об этом с помощью 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(), условие сигнала может быть потеряно.Вы действительно должны ждать в цикле, проверяя некоторые постоянные условия.

...