Itération 122442
Itération 122442

Reputation: 2962

How to properly kill my java program when a scheduled process is done?

I have a simple java program I use to generate elements and insert them in DB every X seconds during a specific time.

The generation is done with a scheduleAtFixedRate. there is only one of these.

I want my program to quit completely when the scheduled task is over. To do so, I use System.exit() when the task is canceled, but is it the correct way to do this ?

Here is my current code:

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);
    }

Upvotes: 2

Views: 759

Answers (2)

Xobotun
Xobotun

Reputation: 1371

I am not sure if this is the correct way to stop the execution of your program if it has some more functions, but I, personally, find it an OK way. :D

So, as it turned out, ScheduledExecutorService seemingly creates non-daemon threads with its default ThreadFactory, perhaps we need to supply a daemonic one to it.

However, if we are to call ExecutorService#shutdown or the forceful ExecutorService#shutdownNow, it will stop both tasks from executing, thus removing the thread(s) that prevent the application from ending its job:

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);
}

Output:

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

Process finished with exit code 0

I hope this will help. :D

Upvotes: 2

Xobotun
Xobotun

Reputation: 1371

This answer is secondary to this one, but it has a different way to solve the problem, so I thought it is appropriate enough to create a separate answer.

If you want to have some more tasks in the future, I believe this solution is more scalable and is more "correct thing".

It creates daemon threads for both runner and interrupter. I think it would be better to create a normal thread factory for the interrupter, but I failed to make it working, so it's better to stick to my first answer...

Here generateForAnHour returns a Future that is used to wait for the time needed.

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);
}

If you won't call Future#get, you'll receive only one Running... in best case or none at all.

If you decide to return the runner future, you'll probably get nasty CancellationException in the get() call:

 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)

I'd use the ExecutorService::shutdown approach as the stabler and more understandable one. :D

Upvotes: 0

Related Questions