Приостановка / остановка и запуск / возобновление Java TimerTask постоянно? - PullRequest
19 голосов
/ 20 января 2010

У меня есть один простой вопрос относительно Java TimerTask. Как приостановить / возобновить две задачи TimerTask в зависимости от определенного условия? Например, у меня есть два таймера, которые работают между собой. Когда определенное условие выполнено в задаче первого таймера, первый таймер останавливается и запускает второй таймер, и то же самое происходит, когда определенное условие выполняется в задаче второго таймера. Класс ниже показывает именно то, что я имею в виду:

public class TimerTest {
    Timer timer1;
    Timer timer2;
    volatile boolean a = false;

    public TimerTest() {
        timer1 = new Timer();
        timer2 = new Timer();     
    }

    public void runStart() {
        timer1.scheduleAtFixedRate(new Task1(), 0, 1000);
    }

    class Task1 extends TimerTask {
        public void run() {
            System.out.println("Checking a");
            a = SomeClass.getSomeStaticValue();

            if (a) {
                // Pause/stop timer1, start/resume timer2 for 5 seconds
                timer2.schedule(new Task2(), 5000);
            }
        }
    }

    class Task2 extends TimerTask{
        public void run() {
            System.out.println("Checking a");
            a = SomeClass.getSomeStaticValue();

            if (!a) {
                // Pause/stop timer2, back to timer1
                timer1.scheduleAtFixedRate(new Task1(), 0, 1000);
            }

            // Do something...
        }
    }

    public static void main(String args[]) {
        TimerTest tt = new TimerTest();
        tt.runStart();      
    }
}

Итак, мой вопрос, как мне сделать паузу timer1 во время работы timer2 и наоборот, когда timer2 работает? Производительность и время - моя главная проблема, так как это должно быть реализовано в другом запущенном потоке. Кстати, я пытаюсь реализовать эти параллельные таймеры на Android.

Спасибо за вашу помощь!

Ответы [ 7 ]

25 голосов
/ 20 января 2010

С TimerTask.cancel():

Обратите внимание, что вызов этого метода из в рамках метода повторения задача таймера абсолютно гарантирует, что задание таймера не запустится снова.

Так что после отмены он больше никогда не запустится. Вместо этого вам лучше использовать более современный ScheduledExecutorService (из Java 5+).

Редактировать: Базовая конструкция:

ScheduledExecutorService exec = Executors.newSingleThreadScheduledExecutor();
exec.scheduleAtFixedRate(runnable, 0, 1000, TimeUnit.MILLISECONDS);

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

TimerTask может быть проще в этом случае, но вам нужно будет создать новый экземпляр при запуске. Его нельзя использовать повторно.

В качестве альтернативы вы можете инкапсулировать каждую задачу как отдельный временный сервис:

final ScheduledExecutorService exec = Executors.newSingleThreadScheduledExecutor();
Runnable task1 = new Runnable() {
  public void run() {
    a++;
    if (a == 3) {
      exec.shutdown();
      exec = Executors.newSingleThreadScheduledExecutor();
      exec.scheduleAtFixedRate(task2, 0, 1000, TimeUnit.MILLISECONDS)
    }
  }
};
exec.scheduleAtFixedRate(task1, 0, 1000, TimeUnit.MILLISECONDS);
15 голосов
/ 20 апреля 2013

самое простое решение, которое я нашел: просто добавьте логическое значение в коде выполнения в задаче таймера, например:

timer.schedule( new TimerTask() {
    public void run() {
       if(!paused){
           //do your thing
       }
    }
 }, 0, 1000 );
7 голосов
/ 20 января 2010

Если вы уже отменили один таймер, вы не можете перезапустить его, вам придется создать новый.

Смотрите этот ответ , он содержит видео и исходный код, как я сделал нечто подобное.

В основном есть два метода: pause и resume

В паузе:

public void pause() {
    this.timer.cancel();
}

В резюме:

public void resume() {
    this.timer = new Timer();
    this.timer.schedule( aTask, 0, 1000 );
}

Это создает ощущение паузы / возобновления.

Если ваши таймеры выполняют разные действияв зависимости от состояния приложения вы можете рассмотреть возможность использования StatePattern

Fist для определения абстрактного состояния:

abstract class TaskState  {
    public void run();
    public TaskState next();
}

и предоставления столько состояний, сколько вам нужно.Ключ в том, что одно государство ведет вас к другому.

class InitialState extends TaskState {
    public void run() {
        System.out.println( "starting...");
    }
    public TaskState next() {
         return new FinalState();
    }
 }
 class FinalState extends TaskState  {
     public void run() {
         System.out.println("Finishing...");
     }
     public TaskState next(){
         return new InitialState();
    }
 }

И затем вы меняете состояние в вашем таймере.

Timer timer = new Timer();
TaskState state = new InitialState();

timer.schedule( new TimerTask() {
     public void run() {
          this.state.run();
          if( shouldChangeState() ) {
              this.state = this.state.next();
           }
     }
 }, 0, 1000 );

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

4 голосов
/ 20 января 2010

На мой взгляд, это несколько ошибочно. Если вашему коду нужны временные гарантии, вы все равно не можете использовать Timer и не захотите. «Этот класс не предоставляет гарантий в реальном времени: он планирует задачи с использованием метода Object.wait (long).»

Ответ, ИМХО, заключается в том, что вы не хотите останавливать и перезапускать свои таймеры. Вы просто хотите подавить их методы запуска от их бизнеса. И это легко: вы просто заключаете их в оператор if. Переключатель включен, они работают, переключатель выключен, они пропускают этот цикл.

Редактировать: Вопрос существенно изменился по сравнению с тем, что было изначально, но я оставлю этот ответ на случай, если он кому-нибудь поможет. Моя точка зрения такова: если вас не волнует, когда ваше событие запускается в интервале N миллисекунд (только то, что оно не ПРЕВЫШАЕТСЯ раз в N миллисекунд), вы можете просто использовать условные выражения в методах выполнения. На самом деле это очень распространенный случай, особенно когда N меньше 1 секунды.

4 голосов
/ 20 января 2010

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

В задании 1:

// Stop timer1 and start timer2
timer1.cancel();
timer2 = new Timer(); // <-- just insert this line
timer2.scheduleAtFixedRate(new Task2(), 0, 1000);

и в задании 2:

// Stop timer2 and start timer1
timer2.cancel();
timer1 = new Timer(); // <-- just insert this other
timer1.scheduleAtFixedRate(new Task1(), 0, 1000);

Он работает на моей машине:

it works!

3 голосов
/ 23 сентября 2014

Android не будет повторно использовать TimerTask, который уже был запланирован один раз.Таким образом, необходимо повторно создать и Timer, и TimerTask, например, так во фрагменте:

private Timer timer;
private TimerTask timerTask;

public void onResume ()
{
    super.onResume();

    timer = new Timer();
    timerTask = new MyTimerTask();
    timer.schedule(timerTask, 0, 1000);
}

public void onPause ()
{
    super.onPause();
    timer.cancel(); // Renders Timer unusable for further schedule() calls.
}
0 голосов
/ 10 июля 2013

Я могу остановить таймер и задачу, используя следующий код:

    if(null != timer)
    {

        timer.cancel();
        Log.i(LOG_TAG,"Number of cancelled tasks purged: " + timer.purge());
        timer = null;
    }

    if(task != null)
    {
        Log.i(LOG_TAG,"Tracking cancellation status: " + task.cancel());
        task = null;
    }
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...