У меня есть простая Java-программа, которую я использую для генерации элементов и вставки их в БД каждые X секунд в течение определенного времени.

Генерация выполняется с scheduleAtFixedRate. есть только один из них.

Я хочу, чтобы моя программа полностью закрывалась по окончании запланированной задачи Для этого я использую System.exit(), когда задача отменяется, но правильный ли это способ?

Вот мой текущий код:

public static void main(String[] args) throws InterruptedException {

        c = generateDbConnection(url, user, password);

        if (c != null) {
            s = generateDbStatement(c);
        } else {
            System.out.println("ERROR");
            return;
        }
        initialTimestamp = new Date();
        TimeUnit.SECONDS.sleep(1);
        generateForAnHour();

    }

    private final static ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);

    /**
     * Generator thread handler Uses Statement from main function
     */
    public static void generateForAnHour() {
        final Runnable runner = new Runnable() {
            public void run() {
                String[][] data = new String[numberOfIds][2];
                for (int i = 0; i < numberOfIds; i++) {
                    data[i] = generateDevice();
                    insertDevice(s, data[i][0], data[i][1]);
                }
                quantityOfIds += numberOfIds;
            }
        };

        final ScheduledFuture<?> generatorHandle = scheduler.scheduleAtFixedRate(runner, 0, 5, TimeUnit.SECONDS);
        scheduler.schedule(new Runnable() {
            public void run() {
                generatorHandle.cancel(true);
                System.out.println("Scheduled ID generator terminated.");
                System.exit(0); //TODO Is it really correct way to do it
            }
        }, timeToRun, TimeUnit.SECONDS);
    }
2
Florian Castelain 20 Дек 2019 в 18:20

2 ответа

Я не уверен, является ли это правильным способом остановить выполнение вашей программы, если у нее есть еще некоторые функции, но лично я считаю, что это нормально. : D

Итак, как оказалось, ScheduledExecutorService, по-видимому, создает потоки, не являющиеся демонами, со значением по умолчанию ThreadFactory, возможно, нам нужно предоставить ему демонический поток.

Однако, если мы вызовем ExecutorService#shutdown или принудительный ExecutorService#shutdownNow, он остановит выполнение обеих задач, удалив тем самым потоки, которые не позволяют приложению завершить свою работу:

private final static ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);

public static void main(String[] args) {
    // Some service code here
    generateForAnHour();
}

public static void generateForAnHour() {
    // Some code that does work
    final Runnable runner = () -> System.out.println("Running...");

    final ScheduledFuture<?> generatorHandle = scheduler.scheduleAtFixedRate(runner, 0, 1, TimeUnit.SECONDS);
    // Code that interrupts the worker after a specified time
    scheduler.schedule(scheduler::shutdown, 5, TimeUnit.SECONDS);
}

Выход:

Running...
Running...
Running...
Running...
Running...
Running...

Process finished with exit code 0

Я надеюсь, это поможет. : D

2
Xobotun 20 Дек 2019 в 16:15

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

Если вы хотите иметь больше задач в будущем, я считаю, что это решение более масштабируемое и более «правильное».

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

Здесь generateForAnHour возвращает Future, который используется для ожидания необходимого времени.

private final static ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1 , new ThreadFactory() {
    @Override
    public Thread newThread(final Runnable r) {
        Thread t = Executors.defaultThreadFactory().newThread(r);
        t.setDaemon(true);
        return t;
    }
});

public static void main(String[] args) throws InterruptedException, ExecutionException {
    // Some service code here

    generateForAnHour().get();
}

public static ScheduledFuture<Boolean> generateForAnHour() {
    // Some code that does work
    final Runnable runner = () -> System.out.println("Running...");
    final ScheduledFuture<?> generatorHandle = scheduler.scheduleAtFixedRate(runner, 0, 1, TimeUnit.SECONDS);

    // Code that interrupts the worker after a specified time
    return scheduler.schedule(() -> generatorHandle.cancel(false), 5, TimeUnit.SECONDS);
}

Если вы не позвоните Future#get, вы получите только один Running... в лучшем случае или ни одного вообще.

Если вы решите вернуть runner будущее, вы, вероятно, получите неприятный CancellationException вызов get():

 Exception in thread "main" java.util.concurrent.CancellationException
    at java.base/java.util.concurrent.FutureTask.report(FutureTask.java:121)
    at java.base/java.util.concurrent.FutureTask.get(FutureTask.java:191)
    at com.xobotun.Test.main(Test.java:30)

Я бы использовал подход ExecutorService::shutdown как более стабильный и понятный. : D

0
Xobotun 20 Дек 2019 в 16:15