Reputation: 1377
I'm currently refactoring a chunk of code that pretty much goes like this:
public static void main(String... args) {
while (true) {
// Do something...
Thread.sleep(300000); // Every 5 minutes
}
}
The idea is that I want to use a ScheduledExecutorService
to do the running of the code:
public static void main(String... args) {
ScheduledExecutorService service = Executors.newSingleThreadScheduledExecutor();
service.scheduleAtFixedRate(new Task(...), 0, 5, TimeUnit.MINUTES);
}
There are a few concerns that I have with this, though: firstly, the fact that the program "terminates" (as in main()
returns) after scheduling, but the JVM is kept alive because the ScheduledExecutorService
remains running. I believe that this is intentional, in order to maintain the old code's behavior of "keep running until terminated from the outside", but I believe that I would have to install a shutdown hook to ensure that the ScheduledExecutorService
shuts down properly. Will that actually be the case, or will "killed from the outside" also prevent execution of the shutdown hook?
Secondly, I'm also squeamish about having a shutdown hook in the first place. If I had code like
try {
while (true) {}
} finally {
// Shutdown hook code goes here instead
service.shutdown();
if (!service.awaitTermination(1, TimeUnit.SECONDS)) {
service.shutdownNow();
}
}
will the same "killed from the outside" concern as above still exist?
EDIT: Yes, the original program loops through without ever breaking through the loop; it was only meant to be terminated from, say, SIGINT (control-C in a terminal). It is a rather poorly-defined termination condition, but that is the only thing that I have right now...
Upvotes: 1
Views: 1556
Reputation: 300
There is a fundamental difference between the 2 approach you are showing. The while(true)
is a synchronous process. You are in control of the task on that main Thread as you execute your routine within. The proposed refactor approach is asychnoniouyly executing the task since you are offloading the task to and executor (hence a new thread).
As mentioned in the other comments, Threads in the executor are created non-daemon. meaning that when a shutdown is called on the application, it will wait to the thread to exit before completing the final shutdown.
So to handle this, there is several things you can do to handle a clean shutdown of you executor. The First thing to do what was mention in the other post, you need to find your termination condition.
Second, you need to evaluate your process and see if you are doing anything critical that cannot be stop mid process (example write to a file on disk).
If you are not doing anything critical (that's the important part here) you can pass a ThreadFactory
that returns a daemon thread. This will make the JVM terminate the process whether or not it is completed. The factory would look something like this (I also added name to it as its always good to name your thread, it makes it easier to debug).
public class DaemonThreadFactory implements ThreadFactory {
private final String NAME;
public DaemonThreadFactory() {
this(null);
}
public DaemonThreadFactory(final String name) {
NAME = name;
}
@Override
public Thread newThread(Runnable r) {
Thread t = new Thread(r);
t.setDaemon(true);
if(NAME != null) {
t.setName(NAME);
}
return t;
}
}
One more way you can handle it is to keep the ScheduledFuture
returned by the service.scheduleAtFixedRate
. With this object, you can cancel the task when you see fit and shutdown the executor at due time.
private void test() {
ScheduledExecutorService exec = Executors.newSingleThreadScheduledExecutor();
ScheduledFuture<?> future = exec.scheduleAtFixedRate(new Task(), 0, 5, TimeUnit.MINUTES);
//if you want to cancel the task
//boolean is if you want to interrupt while running
future.cancel(true);
}
you will still need to shutdown the executor to be clean, but at this point there should be nothing running
if the Future
object is not possible for whatever reason, you will need to use shutdown and shutdownNow if you want the application to shutdown. And if you are doing something critical, you will need a shutdown hook or an asynchronous call in the task in order to properly clean up whatever it is doing and exit gracefully.
Upvotes: 2
Reputation: 200168
When your Java process is killed, the threads will not receive an exception; they'll just abruptly die. So your finally
approach will not help. The shutdown hooks will run, but not if you get killed by SIGKILL. However, the shutdown hook will not be able to do much since the ExecutorService
's pool threads will probably be dead at the time.
In other words, you should better look for a higher-level approach to asking your Java program to finish.
Keep in mind that the current code, with the infinite loop, is on an equal footing with ExecutorService
-based solution with respect to its behavior upon kill signal.
Upvotes: 1
Reputation: 223053
By design, executors run in a non-daemon thread by default. This means that you need some way to tell the executor to terminate, or else your program won't exit.
You need to think about under which conditions you'd want your program to exit. Then, under such conditions, arrange for the executor to shut down.
Upvotes: 0