Таймер Java

Таймер Java

1. Таймер - основы

Timer иTimerTask - это служебные классы Java, используемые для планирования задач в фоновом потоке. В двух словах -TimerTask is the task to perform and Timer is the scheduler.

2. Запланируйте задачу один раз

Начнем сrunning a single task с помощьюTimer:

@Test
public void givenUsingTimer_whenSchedulingTaskOnce_thenCorrect() {
    TimerTask task = new TimerTask() {
        public void run() {
            System.out.println("Task performed on: " + new Date() + "n" +
              "Thread's name: " + Thread.currentThread().getName());
        }
    };
    Timer timer = new Timer("Timer");

    long delay = 1000L;
    timer.schedule(task, delay);
}

Обратите внимание, что если мы запускаем это тест JUnit, мы должны добавить вызовThread.sleep(delay * 2), чтобы разрешить потоку Timer выполнить задачу до того, как тест Junit перестанет выполняться.

3. Запланируйте повторяющееся задание через определенный промежуток времени

Далее - давайтеschedule a task to run at a pre-defined interval.

Мы будем использовать APIscheduleAtFixedRate(repeatedTask, delay, period), который планирует повторное выполнение задачи с фиксированной скоростью, начиная с указанной задержки. Последующие казни происходят через равные промежутки времени, разделенные указанным периодом:

@Test
public void givenUsingTimer_whenSchedulingRepeatedTask_thenCorrect(){
    TimerTask repeatedTask = new TimerTask() {
        public void run() {
            System.out.println("Task performed on " + new Date());
        }
    };
    Timer timer = new Timer("Timer");

    long delay  = 1000L;
    long period = 1000L;
    timer.scheduleAtFixedRate(repeatedTask, delay, period);
}

Обратите внимание, что - если выполнение по какой-либо причине задерживается (например, сборка мусора или другая фоновая активность), два или более выполнения будут выполняться в быстрой последовательности, чтобы «догнать».

3.1. Запланировать ежедневное задание

Далее - давайтеrun a task once a day:

@Test
public void givenUsingTimer_whenSchedulingDailyTask_thenCorrect() {
    TimerTask repeatedTask = new TimerTask() {
        public void run() {
            System.out.println("Task performed on " + new Date());
        }
    };
    Timer timer = new Timer("Timer");

    long delay = 1000L;
    long period = 1000L * 60L * 60L * 24L;
    timer.scheduleAtFixedRate(repeatedTask, delay, period);
}

4. ОтменитеTimer иTimerTask

Выполнение задачи может быть отменено несколькими способами:

4.1. ОтменитьTimerTask внутриRun

Вызывая методTimerTask.cancel() внутри реализации методаrun() самогоTimerTask:

@Test
public void givenUsingTimer_whenCancelingTimerTask_thenCorrect()
  throws InterruptedException {
    TimerTask task = new TimerTask() {
        public void run() {
            System.out.println("Task performed on " + new Date());
            cancel();
        }
    };
    Timer timer = new Timer("Timer");

    timer.scheduleAtFixedRate(task, 1000L, 1000L);

    Thread.sleep(1000L * 2);
}

4.2. ОтменитьTimer

Вызывая методTimer.cancel() для объектаTimer:

@Test
public void givenUsingTimer_whenCancelingTimer_thenCorrect()
  throws InterruptedException {
    TimerTask task = new TimerTask() {
        public void run() {
            System.out.println("Task performed on " + new Date());
        }
    };
    Timer timer = new Timer("Timer");

    timer.scheduleAtFixedRate(task, 1000L, 1000L);

    Thread.sleep(1000L * 2);
    timer.cancel();
}

4.3. Остановить потокTimerTask внутриRun

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

@Test
public void givenUsingTimer_whenStoppingThread_thenTimerTaskIsCancelled()
  throws InterruptedException {
    TimerTask task = new TimerTask() {
        public void run() {
            System.out.println("Task performed on " + new Date());
            // TODO: stop the thread here
        }
    };
    Timer timer = new Timer("Timer");

    timer.scheduleAtFixedRate(task, 1000L, 1000L);

    Thread.sleep(1000L * 2);
}

Обратите внимание на инструкцию TODO в реализацииrun - чтобы запустить этот простой пример, нам нужно будет фактически остановить поток.

В реальной реализации настраиваемого потока должна поддерживаться остановка потока, но в этом случае мы можем игнорировать устаревание и использовать простой APIstop в самом классе Thread.

5. Timer VSExecutorService

Вы также можете использовать ExecutorService для планирования задач таймера вместо использования таймера.

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

@Test
public void givenUsingExecutorService_whenSchedulingRepeatedTask_thenCorrect()
  throws InterruptedException {
    TimerTask repeatedTask = new TimerTask() {
        public void run() {
            System.out.println("Task performed on " + new Date());
        }
    };
    ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();
    long delay  = 1000L;
    long period = 1000L;
    executor.scheduleAtFixedRate(repeatedTask, delay, period, TimeUnit.MILLISECONDS);
    Thread.sleep(delay + period * 3);
    executor.shutdown();
}

Итак, каковы основные различия междуTimer иExecutorService решением:

  • Timer может быть чувствительным к изменениям системных часов; ScheduledThreadPoolExecutor не

  • Timer имеет только один поток выполнения; ScheduledThreadPoolExecutor можно настроить с любым количеством потоков

  • Исключения времени выполнения, возникающие внутриTimerTask, убивают поток, поэтому следующие запланированные задачи не будут выполняться дальше; сScheduledThreadExecutor - текущая задача будет отменена, но остальные продолжат выполнение

6. Заключение

В этом руководстве проиллюстрированы многочисленные способы использования простой, но гибкой инфраструктурыTimer иTimerTask, встроенной в Java, для быстрого планирования задач. Конечно, в мире Java есть гораздо более сложные и полные решения, если они вам нужны - такие какthe Quartz library - но это очень хорошее место для начала.

Реализацию этих примеров можно найти вthe github project - это проект на основе Eclipse, поэтому его должно быть легко импортировать и запускать как есть.