Java while loop и Threads!

У меня есть программа, которая постоянно опроса базы данных для изменения значения какого-либо поля. Он работает в фоновом режиме и в настоящее время использует метод while (true) и sleep() для установки интервала. Мне интересно, если это хорошая практика? И что может быть более эффективным способом реализации этого? Программа предназначена для запуска в любое время.

Следовательно, единственный способ остановить программу состоит в том, чтобы убить идентификатор процесса. Программа может находиться в середине вызова JDBC. Как я могу закончить его более изящно? Я понимаю, что лучшим вариантом было бы разработать какую-то стратегию выхода, используя флаг, который будет периодически проверяться потоком. Но я не могу думать о способе/состоянии изменения значения этого флага. Любые идеи?

Ответы

Ответ 1

Мне интересно, если это хорошая практика?

Нет. Это не хорошо. Иногда это все, что у вас есть, но это не хорошо.

И что может быть более эффективным способом реализации этого?

Как вещи попадают в базу данных в первую очередь?

Лучшее изменение - это исправление программ, которые вставляют/обновляют базу данных, чтобы делать запросы, поступающие в базу данных и в вашу программу. Тема JMS хороша для такого рода вещей.

Следующее лучшее изменение заключается в добавлении триггера в базу данных для размещения каждого события вставки/обновления в очереди. Очередь может передавать тему JMS (или очередь) для обработки вашей программой.

Падение плана - это ваш цикл опроса.

Однако ваш цикл опроса не должен работать тривиально. Он должен отправить сообщение в очередь для выполнения какого-либо другого процесса JDBC. Запрос на завершение - это другое сообщение, которое можно отбросить в очередь JMS. Когда ваша программа получает сообщение о завершении, она обязательно должна быть завершена с предыдущим запросом JDBC и может прекратиться изящно.

Прежде чем делать это, посмотрите на решения ESB. Sun JCAPS или TIBCO уже имеет это. ESB с открытым исходным кодом, например Mulesource или Jitterbit может уже иметь эту функциональность, уже построенную и протестированную.

Ответ 2

Это действительно слишком большая проблема, чтобы полностью ответить на этот вопрос. Сделайте себе одолжение и купите Java Concurrency на практике. Нет лучшего ресурса для Concurrency на платформе Java 5+. Есть целые главы, посвященные этому предмету.

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

Ответ 3

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

Если вы хотите обработать "убийство" процесса немного более изящно, вы можете установить крюк выключения, который вызывается при нажатии Ctrl + C:

volatile boolean stop = false;

Runtime.getRuntime().addShutdownHook(new Thread("shutdown thread") {

    public void run() {

        stop = true;
    }
});

то периодически проверяйте стоп-переменную.

Более элегантным решением является ожидание события:

boolean stop = false;

final Object event = new Object();

Runtime.getRuntime().addShutdownHook(new Thread("shutdown thread") {

    public void run() {

        synchronized(event) {

            stop = true;
            event.notifyAll();
        }
    }
});

// ... and in your polling loop ...
synchronized(event) {

    while(!stop) {

        // ... do JDBC access ...
        try {

            // Wait 30 seconds, but break out as soon as the event is fired.
            event.wait(30000);
        }

        catch(InterruptedException e) {

            // Log a message and exit. Never ignore interrupted exception.
            break;
        }
    }
}

Или что-то в этом роде.

Ответ 4

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

Есть много причин, по которым ваше приложение может умереть. Не сосредотачивайтесь только на одном.

Если теоретически возможно, чтобы ваша работа JDBC оставила вещи в полуправильном состоянии, тогда у вас есть ошибка, которую вы должны исправить. Вся ваша работа с БД должна заключаться в транзакции. Он должен идти или не идти.

Ответ 5

Это Java. Переместите обработку во второй поток. Теперь вы можете

  • Чтение из stdin в цикле. Если кто-то набирает "QUIT", установите флаг while в false и выйдите.
  • Создайте рамку AWT или Swing с кнопкой STOP.
  • Представьте, что вы демон Unix и создаете серверный сокет. Подождите, пока кто-то откроет сокет и отправьте "QUIT". (У этого есть дополнительный бонус, который вы можете изменить сон на выбор с таймаутом.)

На этом должны быть сотни вариантов.

Ответ 6

Настройте обработчик сигнала для SIGTERM, который устанавливает флаг, указывающий, что ваш цикл должен выйти в следующий раз.

Ответ 7

Относительно вопроса "Программа может быть в середине вызова JDBC. Как я могу закончить ее более грациозно?" - см. Как я могу прервать выполняемую транзакцию jdbc?

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

Ответ 8

Если это приложение и вы можете его изменить, вы можете:

  • Сделать это прочитанным файлом
  • Считать значение флага.
  • Когда вы хотите его убить, вы просто изменяете файл, и приложение будет изящно выйти.

Не нужно работать, чтобы это было труднее.

Ответ 9

Я создал класс службы в моей текущей библиотеке служебных программ для этих проблем:

public class Service implements Runnable {

    private boolean shouldStop = false;

    public synchronized stop() {
        shouldStop = true;
        notify();
    }

    private synchronized shouldStop() {
        return shouldStop;
    }

    public void run() {
        setUp();
        while (!shouldStop()) {
            doStuff();
            sleep(60 * 1000);
        }
    }

    private synchronized sleep(long delay) {
        try {
            wait(delay);
        } catch (InterruptedException ie1) {
            /* ignore. */
        }
    }

}

Конечно, это далеко не полный, но вы должны получить суть. Это позволит вам просто вызвать метод stop(), когда вы хотите, чтобы программа остановилась, и она будет полностью завершена.

Ответ 10

Вы можете сделать поле составным значением, которое включает (концептуально) идентификатор процесса и временную метку. [Еще лучше, используйте два или более полей.] Начните поток в процессе, которому принадлежит доступ к полю, и попробуйте его, спать и обновить метку времени. Затем процесс опроса, ожидающий собственного доступа к полю, может заметить, что временная метка не обновилась за какое-то время T (что намного больше времени интервала ожидания цикла обновления) и предположим, что ранее владение процессом умерло.

Но это все еще склонно к сбою.

В других языках я всегда стараюсь использовать вызовы flock() для синхронизации в файле. Не уверен, что такое Java-эквивалент. Получите реальный concurrency, если возможно, возможно.

Ответ 11

Я удивлен, что никто не упомянул механизм прерывания, реализованный на Java. Это должно быть решением проблемы остановки потока. Все остальные решения имеют по крайней мере один недостаток, поэтому этот механизм необходимо реализовать в библиотеке Java concurrency.

Вы можете остановить поток, отправив ему сообщение interrupt(), но есть другие способы прерывания потоков. Когда это происходит, прерывается InterruptedException. Вот почему вы должны обращаться с ним при вызове sleep(), например. Это то, где вы можете сделать очистку и закончить изящно, например, закрыть соединение с базой данных.

Ответ 12

Я думаю, вам следует опросить его с помощью timertask.

Мой компьютер запускает цикл while 1075566 раз за 10 секунд. Это 107557 раз за одну секунду.

Как часто это необходимо для опроса? TimerTask работает быстрее всего 1000 раз за 1 секунду. Вы даете ему параметр в int (miliseconds) в качестве параметров. Если вы довольствуетесь этим, это означает, что вы в течение этого времени напрягаете ваш процессор в 108 раз меньше.

Если вы будете довольны опросом раз в секунду (108 * 1000). 108 000 раз меньше напряжения. Это также означает, что вы можете проверить 108 000 значений с тем же напряжением cpu, которое у вас было с вашим в то время как цикл - потому что вы не назначаете свой процессор часто проверять. Помните, что процессор имеет тактовый цикл. Шахта составляет 3 600 000 000 герц (циклы в секунду).

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

Вы также можете использовать thread.sleep(miliseconds);, чтобы снизить нагрузку на ваш опросный поток (поскольку он обычно не будет опросить вас).

Ответ 13

Java9 имеет еще один "потенциальный" ответ на этот вопрос: Thread.onSpinWait():

Указывает, что вызывающий абонент мгновенно не может прогрессировать, до появления одного или нескольких действий со стороны других действий. Вызывая этот метод в каждой итерации цикла цикла spin-wait, вызывающий поток указывает во время выполнения ожидание. Среда выполнения может предпринять меры для повышения производительности при построении циклов цикла spin-wait.

Подробнее см. JEP 285.