Запуск потока Java с интервалами

У меня есть поток, который нужно выполнять каждые 10 секунд. Этот поток содержит несколько обращений (12-15) к базе данных на другом сервере. Кроме того, он также получает доступ к примерно 3 файлам. Следовательно, будет довольно много операций ввода-вывода и сетевых накладных расходов.

Какова наилучшая стратегия для выполнения вышеперечисленного?

Одним из способов было бы использование метода сна вместе с циклом while, но это был бы плохой дизайн.

Поможет ли в этом случае класс, похожий на Timer? Кроме того, было бы лучше создать еще пару потоков (один для ввода-вывода и один для JDBC) вместо того, чтобы запускать их в одном потоке?


person Epitaph    schedule 09.01.2009    source источник
comment
Почему сон плохой дизайн? Конечно, это будет не каждые 10 секунд, а каждые 10+n секунд. Но вы все равно не можете гарантировать 10 секунд, если один из проходов занимает 12 секунд. Если вы просто пытаетесь минимизировать нагрузку на сервер, не имеет большого значения, будет ли это сон или таймер.   -  person paxdiablo    schedule 09.01.2009
comment
Я имел в виду сон вместе с циклом while как плохой дизайн. Поскольку решение о том, когда и как завершать поток, может быть не очень интуитивным.   -  person Epitaph    schedule 09.01.2009


Ответы (3)


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

ScheduledExecutorService executor =
    Executors.newSingleThreadScheduledExecutor();

Runnable periodicTask = new Runnable() {
    public void run() {
        // Invoke method(s) to do the work
        doPeriodicWork();
    }
};

executor.scheduleAtFixedRate(periodicTask, 0, 10, TimeUnit.SECONDS);
person Greg Case    schedule 09.01.2009
comment
Если бы вы не использовали исполняющую программу с одним потоком, я мог бы заключить вызов doPeriodicWork() в синхронизированный (этот) блок на всякий случай. - person Evan; 09.01.2009
comment
Это, безусловно, защитит; объявление doPeriodicWork синхронизированным сделает то же самое и, вероятно, будет немного чище. При этом, вероятно, в doPeriodicWork есть более мелкие критические секции, которые можно было бы защитить по отдельности при перемещении в пул потоков. - person Greg Case; 09.01.2009
comment
@GregCase: Если вы измените periodicTask во время работы executor, как заставить executor обновлять себя, чтобы использовать последнюю версию periodicTask во время выполнения? - person ThreaT; 04.08.2015
comment
@ThreaT: нет возможности напрямую заменить экземпляр Runnable. У вас есть 2 варианта: отменить (используя ScheduledFuture, возвращаемый по расписанию) и повторно отправить с новым Runnable, или настроить экземпляр Runnable так, чтобы он делегировал его другому экземпляру объекта, а затем заменял этот экземпляр объекта на лету. Обратите внимание, что если вы сделаете это, ваш Runnable, скорее всего, не сможет быть анонимным внутренним классом, как пример в ответе; вам нужно будет как минимум объявить поле, хранящее экземпляр делегата, и сохранить ссылку где-нибудь, чтобы иметь возможность обновлять это поле. - person Greg Case; 04.08.2015

Один из вариантов — создать ScheduledExecutorService, для которого вы можете запланировать свою работу:

ScheduledExecutorService ex = Executors.newSingleThreadScheduledExecutor();
ex.scheduleWithFixedDelay(...);

Если вы решили использовать несколько потоков, вы можете создать ScheduledExecutorService с большим количеством потоков (опять же, через класс Executors).

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

  • для вашего конкретного приложения может ли один поток действительно «работать», пока другой ожидает ввода-вывода?
  • будут ли ваши несколько потоков в конечном итоге «загружать один и тот же ресурс» (например, читать из файлов в разных местах на одном и том же dsk) и, таким образом, замедлять друг друга, или они будут одновременно использовать разные ресурсы?
person Neil Coffey    schedule 09.01.2009

Взгляните на Таймер и < классы href="http://java.sun.com/javase/6/docs/api/java/util/TimerTask.html" rel="nofollow noreferrer">TimerTask. Они именно то, что вы хотите.

Вы можете сделать реализацию TimerTask, которая принимает ваш объект потока в конструкторе.

Затем метод запуска вызовет метод запуска потоков.

// Perhaps something like this
Timer t = new Timer();
t.scheduleAtFixedRate(yourTimerTask, 0, 10 * 1000);
// Hopefully your task takes less than 12 seconds
person jjnguy    schedule 09.01.2009
comment
10 символов - это просто предложение! - person jjnguy; 09.01.2009
comment
Я только что пытался войти! . и я получаю страшное Пожалуйста, введите не менее 10 символов. под полем ввода, когда я нажимаю «Добавить комментарий». На самом деле он не входит в комментарий. Как ты это сделал?? - person paxdiablo; 09.01.2009
comment
Если вы наберете !‹много пробелов›. можно обмануть систему. Кажется, он считает пробел символом, а затем удаляет лишние, когда считает нужным. - person jjnguy; 09.01.2009
comment
Да, нельзя ставить пробелы в начале или в конце, они должны быть посередине! Кто написал этот бред? :-) - person paxdiablo; 09.01.2009