我正在尝试使用ctrl-c键盘快捷键关闭我的服务器,我搜索了一些方法来执行此操作并找到this帖子。
我尝试了这个解决方案,但是当关闭钩子完成时,我的主线程也会在没有完成服务器关闭任务的情况下结束,并且该过程以代码1结束。
这是我的代码:
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");
}
}
}
这是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
我如何优雅地解决这个问题?
当关闭钩子完成时,我的主线程也会在没有完成服务器关闭任务的情况下结束
可能在关机挂钩完成之前。这是正确的行为。主线程被JVM强制终止。这是CTRL / C的影响。
这也意味着你的关机钩子基本没有意义。你可以完全删除它,主线程仍然会以相同的方式退出。
“服务器关闭任务”应该在关闭钩子中。这就是它的用途。但它们不应该阻塞或长时间运行:请参阅Javadoc。
NB ServerSocket.accept()
不会返回null。不要写无意义的测试。
我认为这里发生的事情是关闭钩子的语义没有很好地记录(在官方文档或博客文章中找不到这个)。
我认为它发生的是JVM在所有关闭钩子线程返回后(在接收到信号的情况下)立即终止,而不等待主线程完成。
您可以使用以下小程序模拟此项:
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();
}
}
}
也没有线程中断发生:我认为线程实际上只是终止了。
您可能想要做什么(我在某处看到类似的代码,但现在无法回想起)是获取对主线程的引用并在关闭钩子中加入它。这样的东西适合我:
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();
}
}
}
这打印(按ctrl + c后)以下内容:
^CShutdown signal
Finished
Joined on main thread.