2012-04-23 4 views
2

У меня есть нить, которая делает несколько вещей. Один из них - просто спать в течение некоторого времени. После нормального сна он вызывает метод delayFinished(), но если спящий прерван, то delayFinished() не следует вызывать. Мне также нужен метод, который прерывает сон, который может быть вызван другими потоками.Pure Java: Как узнать, спал ли мой поток или нет?

Так это реализация, которая захватывает мое намерение, но я не думаю, что это будет работать:

public class MyThread extends Thread { 
    private boolean sleeping=false; 
    private Object sleepingControl=new Object(); 

    //... other unrelated stuff... 

    private void delay() { 
     try { 
      synchronized(sleepingControl) { 
      sleeping=true;   
      sleep(delay); 
      sleeping=false;       
      delayFinished(); 
      } 
     } catch (InterruptedException e) { 
      sleeping=false; 
     } 
    } 
    public void abortDelay() { 
      synchronized(sleepingControl) { 
      if (sleeping)   
       interrupt(); 
      } 
    } 
} 

Если delay() называется, и в то время как он спит, abortDelay() вызывается другим потоком (основной используйте случай), abortDelay() будет висеть на синхронизированном заявлении, так как вызывающий delay() владеет этим монитором и еще не отказался от него.

С другой стороны, если задержка реализуется следующим образом:

private void delay() { 
     synchronized(sleepingControl) { 
     sleeping=true;   
     } 
     try {    
      sleep(delay); 

вполне возможно, что delay() называется, заканчивает синхронизированный блок настройки сна, чтобы верно, но тогда abortDelay() называется, и было бы назвать interrupt() , хотя нить еще не начала спать.

Может ли кто-нибудь предложить какие-либо улучшения этих попыток?

ответ

6

Вам нужно исследовать Object.wait()/notify() вместо использования sleep(). С wait() одним из важных моментов является то, что он освобождает блокировку объекта, ожидая, пока другой поток не получит блокировку и не забудет ее с notify().

например.

public class MyThread extends Thread { 
    private boolean aborted = false; 
    private final Object sleepingControl=new Object(); 

    //... other unrelated stuff... 

    private void delay() { 
     try { 
      synchronized(sleepingControl) { 
      sleepingControl.wait(delay); 

      if (!aborted) 
       delayFinished(); 
      } 
     } catch (InterruptedException e) { 
     } 
    } 
    public void abortDelay() { 
      synchronized(sleepingControl) { 
      aborted = true; 
      sleepingControl.notify(); 
      } 
    } 
} 

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

В действительности, я думаю, что вы можете достичь вышеизложенного с помощью классов java.util.concurrent. Взгляните на класс Lock.

+0

Действительно ли это «синхронизированный» материал? –

+0

'wait() имеет любопытную реализацию quirk, где он может ложно просыпаться »- я подозреваю, что только на ОС с примитивами синхронизации, которые работают неправильно. –

+0

Если вы вызываете delay() более одного раза (из любого места внутри класса), может возникнуть проблема с пониманием того, какой из вызовов должен быть прерван с помощью метода abortDelay() –

0

Ответ Майка К. является тем, который вы хотите. Но чтобы заставить ваш код работать, сделайте sleeping volatile и удалите синхронизацию с abortDelay. Тогда ваша единственная проблема заключается в том, что abortDelay может поймать delay непосредственно перед любой линией sleeping = false;. Поэтому после обеих строк вызовите interrupted(), чтобы очистить возможные настройки.

Исправление/разработка:abortDelay требуется синхронизация. Код должен выглядеть так:

private final Object  sleepingControl = new Object(); 
private volatile boolean sleeping; 

private void delay() { 
    try { 
     sleeping=true; // Synching not needed. 
     sleep(delay); 
     // Thread COULD be interrupted at this point! 
     // Now makes sure abortDelay sees this change. 
     synchronized (sleepingControl) { 
      sleeping = false; 
      // Thread can no longer be interrupted. 
      // Clear flag if it is set. 
      interrupted(); 
     } 
     delayFinished(); 
    } 
    catch (InterruptedException e) { 
     // Thread COULD be interrupted at this point! 
     synchronized (sleepingControl) { 
      sleeping = false; 
      // Thread can no longer be interrupted. 
      // Clear flag if it is set. 
      interrupted(); 
     } 
    } 
} 
public void abortDelay() { 
    synchronized (sleepingControl) { 
     if (sleeping) 
      // At this point, "sleeping" HAS to be true. 
      interrupt(); 
    } 
} 
+0

Ральф, ваш ответ непонятен. особенно то, что вы подразумеваете под «до любой строки ...», но: если я удалю синхронизацию из abortDelay, в чем смысл держать ее в задержке()? Кроме того, не уверен, что вы подразумеваете под «после того, как обе строки вызвали прерывание()», но я думаю, что это проблема, если вы вызываете прерывание(), когда поток не прерывается (а не во сне). поэтому без «синхронизированного», это проблема, если abortDelay() вызывается точно до или после sleep(). если вы считаете, что у вас есть хорошее исправление для моего кода, пожалуйста, отправьте фиксированный код. Мне было бы интересно. спасибо – inor

+0

@inor: Я получил мои синхронизированные удаления неправильно; вы нуждаетесь в них в 'abortDelay'. Я отредактирую свой ответ, чтобы включить то, что я считаю правильным решением. Вы _can_ всегда прерываете поток; он просто устанавливает флаг. 'sleep' видит, что вы это сделали, и выбрасывает исключение. Вам нужно вызвать 'прерывание ', чтобы очистить его, если поток« прерван »после вызова' sleep'. Мое первоначальное предложение могло прервать вашу нить в любое время, если, скажем, поток abortDelay проверял «спящий», получил приостановление на полчаса, а затем вызвал «прерывание». Нехорошо. – RalphChapin

+0

@inor: Я пытаюсь показать, что ваше решение может работать. Использование wait-modify _is_ больше стандартного способа справиться с этим. – RalphChapin

Смежные вопросы