Reputation: 2962
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
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
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