сброс таймаута в Java - PullRequest
       17

сброс таймаута в Java

5 голосов
/ 26 января 2010

(аналогично «Сбрасываемый таймер Java» , но есть некоторые тонкости, которые мне нужно изучить)

Мне нужна функция сбрасываемого тайм-аута, так что если мой класс не выполняет определенныйдействие в интервале времени T0 (где T0 находится в окрестности 50-1000 мсек), затем вызывается метод:

class MyClass {
    static final private timeoutTime = 50;
    final private SomeTimer timer = new SomeTimer(timeoutTime, 
        new Runnable () { public void run() {
            onTimeout();
        }});

    private void onTimeout() { /* do something on timeout */ }

    public void criticalMethod() { this.timer.reset(); }
}

Что я могу использовать для реализации этого?Я знаком с ScheduledExecutorService и с идеей вызова ScheduledFuture.cancel () , а затем переназначения задачи кажется такой, как будто она должна работать, но естьпотенциальная опасность, если метод cancel () завершится неудачно и запланированное задание будет выполнено, когда оно не должно.Я чувствую, что мне здесь не хватает тонкости.

Также (возможно, более важно), есть ли способ проверить мою реализацию / доказать, что она работает правильно?

edit: Меня особенно беспокоит случай, когда criticalMethod() часто вызывается (возможно, несколько раз в миллисекунду) ... если я использую ScheduledExecutorService, это просто кажется потенциальной проблемой с ресурсами, чтобы продолжать создавать новые запланированные задачи + отменять старыевместо прямого способа перенести задачу.

Ответы [ 2 ]

7 голосов
/ 27 января 2010

хорошо, вот попытка использования ScheduledExecutorService. Я впечатлен производительностью; Я запустил тестовую программу с аргументами 50 1 10 (тайм-аут 50 мс; каждые 1 мс ResettableTimer сбрасывается 10 раз), и он практически не использует мой ЦП.

package com.example.test;

import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;

public class ResettableTimer {
    final private ScheduledExecutorService scheduler;
    final private long timeout;
    final private TimeUnit timeUnit;
    final private Runnable task;
    final private AtomicReference<ScheduledFuture<?>> ticket
        = new AtomicReference<ScheduledFuture<?>>();
    /* use AtomicReference to manage concurrency 
     * in case reset() gets called from different threads
     */

    public ResettableTimer(ScheduledExecutorService scheduler, 
            long timeout, TimeUnit timeUnit, Runnable task)
    {
        this.scheduler = scheduler;
        this.timeout = timeout;
        this.timeUnit = timeUnit;
        this.task = task;
    }

    public ResettableTimer reset(boolean mayInterruptIfRunning) {
        /*
         *  in with the new, out with the old;
         *  this may mean that more than 1 task is scheduled at once for a short time,
         *  but that's not a big deal and avoids some complexity in this code 
         */
        ScheduledFuture<?> newTicket = this.scheduler.schedule(
                this.task, this.timeout, this.timeUnit);
        ScheduledFuture<?> oldTicket = this.ticket.getAndSet(newTicket);
        if (oldTicket != null)
        {
            oldTicket.cancel(mayInterruptIfRunning);
        }
        return this;
    }


    static public void main(String[] args)
    {
        if (args.length >= 3) 
        {
            int timeout = Integer.parseInt(args[0]);
            int period = Integer.parseInt(args[1]);
            final int nresetsPerPeriod = Integer.parseInt(args[2]);
            ScheduledExecutorService scheduler = new ScheduledThreadPoolExecutor(1);
            final ResettableTimer timer = new ResettableTimer(scheduler, 
                    timeout, TimeUnit.MILLISECONDS,
                    new Runnable() { 
                        public void run() { System.out.println("timeout!"); }
                    }
            );

            // start a separate thread pool for resetting
            new ScheduledThreadPoolExecutor(5).scheduleAtFixedRate(new Runnable() {
                private int runCounter = 0;
                public void run() { 
                    for (int i = 0; i < nresetsPerPeriod; ++i)
                    {
                        timer.reset(false);
                    }
                    if ((++this.runCounter % 100) == 0)
                    {
                        System.out.println("runCounter: "+this.runCounter);
                    }
                }
            }, 0, period, TimeUnit.MILLISECONDS);

            try 
            {
                while (true)
                {
                    Thread.sleep(1000);
                }
            }
            catch (InterruptedException e)
            {
                System.out.println("interrupted!");
            }
        }
    }
}
3 голосов
/ 26 января 2010

Отмененный атрибут прикреплен к объекту задачи. Итак, либо задача не началась, когда вы вызываете cancel, и она не запустится; или задача уже началась при вызове cancel, и она прерывается.

Как справиться с прерыванием зависит от вас. Вам следует регулярно опрашивать Thread.interrupted() (который, кстати, сбрасывает флаг прерывания, так что будьте осторожны), если вы не вызываете никакую прерываемую функцию (те, которые объявляют InterruptedException в своем предложении throws).

Конечно, если вы вызываете такие функции, вы должны разумно обработать InterruptedException (включая восстановление флага прерывания (Thread.currentThread().interrupt()) до того, как ваша задача вернется). : -)

Чтобы ответить на ваши изменения, создание объекта обходится дешево, если у вашего объекта мало состояния. Лично я не стал бы беспокоиться об этом слишком сильно, если бы профилирование не показало, что это узкое место.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...