Reputation: 1164
I'm trying to shutdown my server using a ctrl-c keyboard shortcut and I searched for some way to do this and found this post.
I tried this solution, but when the shutdown hook finishes, my main thread also ends up without complete the server shutdown tasks and the process finishes with code 1.
Here is my code:
package test;
import java.io.IOException;
import java.net.ServerSocket;
public class Main implements Runnable {
private ServerSocket serverSocket;
private Main() {
try {
serverSocket = new ServerSocket(5000);
} catch (IOException e) {
e.printStackTrace();
System.exit(10);
}
System.err.println("Waiting ctrl-c ...");
Runtime.getRuntime().addShutdownHook(new Thread(this));
try {
while (serverSocket.accept() != null) {
doSomething();
}
System.err.println("end while");
} catch (IOException e) {
System.err.println("end exception");
} finally {
System.err.println("trying to close the server...");
try {
// simulate a long job
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// this line was never reached
System.err.println("server closed");
}
}
public static void main(String[] args) {
new Main();
}
private void doSomething() {}
@Override
public void run() {
System.err.println("ctrl-c pressed");
try {
System.err.println("trying to close socket");
serverSocket.close();
System.err.println("socket closed!!!");
} catch (IOException e) {
System.err.println("failed to close socket");
}
}
}
Here is the output from inside IntelliJ IDE:
Waiting ctrl-c ...
ctrl-c pressed
trying to close socket
socket closed!!!
end exception
trying to close the server...
Process finished with exit code 1
How do I, elegantly, fix this problem?
Upvotes: 1
Views: 2055
Reputation: 135
I think what's happening here is that the semantics of the shutdown hook is not very well documented (couldn't find this anywhere in official docs or in blog posts).
What I think it happens is that the JVM terminates immediately after all shutdown hooks threads have returned (in case of receiving a signal) without waiting for main thread to finish.
You can simulate this with the following small program:
package cosenmarco;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
public class Main {
private static volatile CompletableFuture<Void> shutdownFuture = new CompletableFuture<>();
public static void main(String[] args) {
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
shutdownFuture.complete(null);
System.err.println("Shutdown signal");
}));
try {
shutdownFuture.get();
Thread.sleep(1000); // Comment this to have "Finished" correctly printed
System.err.println("Finished");
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
Also no interruption of the thread happens: I think the thread is actually just terminated.
What you may want to do (I've seen similar code somewhere but cannot recall it right now) is to get a reference to the main thread and join on it in the shutdown hook. Something like this works for me:
package cosenmarco;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
public class Main {
private static volatile CompletableFuture<Void> shutdownFuture = new CompletableFuture<>();
public static void main(String[] args) {
Thread mainThread = Thread.currentThread();
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
shutdownFuture.complete(null);
System.err.println("Shutdown signal");
try {
mainThread.join();
System.err.println("Joined on main thread.");
} catch (InterruptedException e) {
e.printStackTrace();
}
}));
try {
shutdownFuture.get();
Thread.sleep(1000);
System.err.println("Finished");
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
This prints (after pressing ctrl+c) the following:
^CShutdown signal
Finished
Joined on main thread.
Upvotes: 1
Reputation: 310860
when the shutdown hook finishes, my main thread also ends up without complete the server shutdown tasks
Probably before the shutdown hook finishes. This is correct behaviour. The main thread is forcibly terminated by the JVM. That's the effect of CTRL/C.
That also means that your shutdown hook is basically pointless. You could remove it completely and main thread will still exit the same way.
The 'server shutdown tasks' should be in the shutdown hook. That's what it's for. But they should not be blocking or long-running: see the Javadoc.
NB ServerSocket.accept()
doesn't return null. Don't write pointless tests.
Upvotes: 1