2011-12-27 6 views
6

Я просматриваю код Java, который по существу является повторяющимся процессом, который перемещает/читает/анализирует некоторые файлы на регулярной основе и выводит данные в базу данных. Повторяющаяся часть делается (грубо) следующим образом:Java - Thread.sleep в основном методе

public class CollectorMain { 
    public static boolean signalRecieved = false; 
    public static void main(String[] args) { 
     Runtime.getRuntime().addShutdownHook(new Thread() { 
       public void run() { 
       shutdown(); 
     }}); 
     while(!signalRecieved) { 
       Collector.execute(); 
       try { 
        Thread.sleep(60 * 1000); 
       } catch (InterruptedException e) { 
        break; 
       } 
     } 
     // some shutdown logic 
    } 
    public static void shutdown() { 
     signalReceived = true; 
    } 
} 

public class Collector() { 
    public static void execute() { 
     // Move files from the queue dir to temp location 
     // Read, parse files and insert records into database. 
     // Then delete the processed files 
    } 
} 

Моя рекомендация реорганизовать код

  1. Создать экземпляр коллектора и реорганизовать статический метод Execute() к методу экземпляра
  2. Для используйте Runnable или TimerTask для обработки вызовов.

Мой аргумент заключался в том, что использование Thread.wait из основного метода и объединение его со статическим доступом не является хорошим способом o f обрабатывает повторяемый процесс, особенно делая файл IO. На что автор ответил (цитирую)

Описание Runnable говорит «должен быть реализован любым классом , экземпляры которого предназначены для исполнения на волоске». Фактически, I умышленно избегает потоков в этой программе по соображениям стоимости vrs.performance requirements.

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

Технически, Java не выполняется вообще, это интерпретируется JVM , который затем выполняет машинные команды, для имитации того, что выполняется код Java . Так что это действительно JVM, который выполняется в потоке или нескольких потоках.

Но, как писатель кода Java, мне все равно. Если я не создаю «потоки» в Java, то это будет работа JVM, как будто нет ни одного потоков, даже если JVM использует потоки «под обложками».

Пауза Java не выполняется, она имитируется последовательностью команд , которые могут или не могут вызывать «ожидание» ОС. (Вероятно, это , потому что JVM не захочет вращаться, сжигая циклы процессора, но это выбор реализации JVM).

Так что у меня 2 вопроса:

  1. ставит Thread.wait в основной метод законны, безопасным и целесообразным способом делать повторяемые задания в этом случае? А если нет, то почему нет, так как есть только один (основной) поток исполнения?
  2. Каковы погрешности использования статических методов в этом контексте (если есть)?

Я буду рад предоставить дополнительную информацию, если у вас есть другие вопросы.

+0

Не следует ли защищать 'signalRecieved' (например,' volatile'), так как он разделен между основным потоком и крюком отключения? – dacwe

+0

Хорошая мысль, идет к идее «не использовать несколько потоков» – Bostone

+0

Завершение крючков __are__ отдельных потоков. – toto2

ответ

6

Вы действительно спорили о проектных решений, а не решений производительности.

Утверждение вашего коллеги о том, как реализована реализация Thread.sleep, в основном неверно, насколько я могу судить. На разумной JVM в разумной операционной системе Thread.sleep() реализуется с использованием собственного метода O/S для парковки потока (или помещения его в состояние «время ожидания» или что угодно, что вы хотите назвать его в своей ОС) , Или по-другому, пока поток спал, он потребляет нулевой процессор. И в любом случае TimerTask будет использовать Thread.sleep (или аналогичный), я не просто помню, использует ли он метод park(), введенный в Java 5, но по существу не имеет значения).

JVM, как правило, не делает секретных решений о потолочной обработке. Если вы попросите другой поток, вы получите его; если вы этого не сделаете, вы не будете. Для сбора мусора и т. Д. Будет создано несколько «домашних» потоков, но, насколько ваш код, вы можете предположить, что не создается тайное умное создание потоков.

Итак, возвращаясь на вопросы:

  • спящих в основном потоке совершенно безопасна сама по себе; очевидно, в то время как основной поток спал, он ничего не будет выполнять, но если вам это не нужно, тогда все в порядке;
  • независимо от того, используете ли вы Thread.sleep() непосредственно в своем коде или используете один из методов утилиты «таймер», также является конструктивным решением, основанным на том, хотите ли вы использовать общую библиотеку, которая удаляет данные реализации из вашего кода, или вы предпочитаете иметь эти детали и под вашим контролем; с точки зрения производительности, это будет иметь небольшие шансы, если вы правильно реализуете вещи;
  • У вас есть статический метод или у вас есть экземпляр Collector, который не имеет особого значения - это просто дизайнерское решение, которое вам нужно сделать на основе того, что кажется более интуитивным: вы видите Collector как класс «общая полезность» , или как «вещь» с условием, что вы запрашиваете выполнение операций?
+0

Мне нравится этот ответ. Я буду ждать обсуждения, чтобы продолжить немного, но на данный момент ваш - мой любимый ответ. – Bostone

0

В примере кода размещен, я хотел бы использовать Object.wait(60 * 1000) вместо Thread.sleep(60 * 1000), то в методе shutdown, добавьте вызов notifyAll() после установки signalReceived = true.Это потребует добавления необходимых блоков синхронизации вокруг методов уведомления и ожидания. Как минимум, это обеспечит преимущество «цикла ожидания», позволяющего немедленно выйти, вместо того, чтобы ждать, пока таймаут завершится первым. (См ответ JB Nizet для некоторых дополнительных деталей вокруг этого.)

С общей точки зрения - я бы Коллектор реализовать TimerTask (суб-интерфейс Runnable), планировать его с Timer, и сделать с ней.

Некоторые пользователи будут защищать использование статических по соображениям производительности, что, по моему мнению, будет в основном историческим. Сохранение всех нестатических позволит использовать в будущем несколько экземпляров в рамках одной JVM, а также возможность использования подклассов для переопределения методов и настройки базовой реализации.

+1

Нет способа Thread.wait. –

+0

@JBNizet - Исправлено. Вы правы, 'wait' объявлен в' Object', но технически Thread имеет его также через наследование из 'Object'. :-) – ziesemer

+0

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

3

Вы вводите в заблуждение sleep и wait. sleep делает текущий поток незанятым в течение некоторого периода времени, а затем поток перезапускается самостоятельно. wait - метод объекта. Он используется, чтобы поместить поток в состояние ожидания, и он выйдет только из этого состояния, если другой поток разбудит его, используя notify или notifyAll на том же объекте. Использование wait, если только один поток выполняется, просто заставит вашу программу вечно существовать.

Читать http://docs.oracle.com/javase/tutorial/essential/concurrency/index.html

+0

Использование переопределенного метода 'wait', который принимает тайм-аут, может использоваться как альтернатива' sleep', позволяя ему возвращаться либо по вызову 'notify' (' All'), либо по истечении таймаута. – ziesemer

+1

Во всяком случае, мне кажется, что затвор отключения не имеет никакой цели, так как он сам не выполняет shutdown, но только просит основной поток сделать это, изменив логическую переменную. JVM будет остановлен, как только закончится последний цикл завершения работы. Таким образом, он мог остановиться до или в середине логики отключения. Если крюк остановки не полезен, остается только один поток, и ожидание в этом случае не имеет смысла. –

1

Я думаю, что ваш коллега на самом деле не понимает JVM и его модель резьбы; ваш код не будет волшебно многопоточным, если вы явно не сделаете это именно так.Кроме того, я думаю, что вы оба слишком много работаете над этим. Дайте библиотеке Quartz: http://quartz-scheduler.org/documentation/quartz-2.1.x/quick-start

Мое рассуждение о том, что Java Threading трудно получить правильно, особенно когда вы обнаруживаете, что выполняете ожидание/уведомление самостоятельно и сражаетесь со всеми случаями краев. Библиотека кварца полностью отбросила все это и поставила повторяющийся аспект за привычной моделью CRON.

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

Вызов статических методов или нет - это неважно, независимо от вашего решения, ключ понимает, какое состояние делится между потоками, а также устраняет общий доступ или синхронизирует доступ к нему, поэтому все потоки получают согласованное представление данных. Если бы я был на вашем месте, я бы отдал Кварцу выстрел. Если вы неохотно добавляете еще одну библиотеку, JDK 1.5 (я думаю) привел в ScheduledExectutorService, который, как я думаю, выполняет повторяющиеся задачи.

Независимо от того, в какую сторону вы идете, не составляйте структуру планирования/экзекуции самостоятельно. Это решаемая проблема в Quartz или ScheduledExectutorService.

+0

Почему вы думаете, что здесь нужна резьба? – jdigital

+0

Кварц был в моем списке рекомендуемых решений – Bostone

+0

Runnable in Java - это абстракция задачи, которая должна выполняться в потоке. Каждая программа, которая работает в Java, делает это в потоке, будь то главный поток или какой-либо другой поток. Задача, которую парень DroidIn.net дал нам, как лучше всего справляться с повторяющейся задачей в его системе. Это заставило меня подумать о Runnable и о его отношении к Threads. Threading не требуется, но эта проблема довольно хорошо объясняется механизмами Threading JDK, их расширениями, такими как ScheduledExectutorService, или библиотекой типа Quartz. Не пишите этот код самостоятельно. –

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