【发布时间】:2016-07-01 22:32:06
【问题描述】:
我有一个在命令窗口中运行的应用程序。我有一个Scanner,它检测用户何时键入/quit,并运行相应的quit() 方法。
这很好用,但是有没有办法确定用户是否在不输入/quit 的情况下关闭控制台,并且仍然运行quit()?
【问题讨论】:
我有一个在命令窗口中运行的应用程序。我有一个Scanner,它检测用户何时键入/quit,并运行相应的quit() 方法。
这很好用,但是有没有办法确定用户是否在不输入/quit 的情况下关闭控制台,并且仍然运行quit()?
【问题讨论】:
使用关闭挂钩。仍然不能 100% 保证它会被调用,但这是您尝试关闭的最佳方法。一般来说,你应该在关闭钩子中做尽可能少的事情。关闭资源,删除一些日志语句或打印语句,但不要做任何需要很长时间的事情。
我已经复制并稍微修改了 Robert 在此处找到的答案 Detecting when a Java application closes 以匹配您的代码。您应该将此代码放在您有权调用quit() 的地方。您应该只调用一次,但您应该在整个过程的早期调用它。
Runtime.getRuntime().addShutdownHook(new Thread() {
@Override
public void run() {
quit();
}
});
有关关闭挂钩功能的更多详细信息,请参阅此问题:Useful example of a shutdown hook in Java?
请参阅此问题的已接受答案,了解有关何时调用和不调用关闭挂钩的更多详细信息 shutdown hook vs finalizer method
请参阅此问题以获取有关您需要多长时间使用 quit() TL;DR 的信息 - 这取决于 How long does the JVM allow shutdown hooks to run before calling halt?
【讨论】:
Thread.sleep(),它仍然立即关闭。在做任何事情之前,我会看看是否还有其他答案。不过谢谢! =D @Jeutnarg
这是捕获程序退出的风险更大但更强大的方法。有几种方法可以故意破坏这种方法,并且操作系统也可能会意外破坏它。这就是我说“风险更大”的原因。
您创建一个类,即 Bodyguard,它计算出自己的进程 ID,然后以进程 ID 作为输入运行 RealProgram。 Bodyguard 然后等待 RealProgram 关闭,然后终止。同时,RealProgram 监视并检查 Bodyguard 的进程是否仍在运行。如果它看到 Bodyguard 已经死亡,那么它会退出它的主循环并在终止之前调用 quit()。
我已经在 Windows 10 上对此进行了测试,运行 Java 8,并从 Eclipse 调试器中杀死了 Bodyguard。当 Bodyguard 被杀死时,RealProgram 会在调用 quit() 后关闭。当 RealProgram 被杀死时,Bodyguard 也会终止。
由于我认为您会想玩弄代码,因此我将一些调试元素(例如编写器)留在了那里。
我相信:
保镖类:
package so_38154756;
import java.lang.reflect.InvocationTargetException;
public class Bodyguard {
public static void main(String[] args){
Bodyguard lamb = new Bodyguard();
lamb.initiateSacrifice();
}
private void initiateSacrifice() {
try{
int ownPid = getPID();
runRealProgram(ownPid);
} catch(Exception e){
System.out.println("Sorry, this isn't working.");
}
}
private int getPID() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
java.lang.management.RuntimeMXBean runtime = java.lang.management.ManagementFactory.getRuntimeMXBean();
java.lang.reflect.Field jvm = runtime.getClass().getDeclaredField("jvm");
jvm.setAccessible(true);
sun.management.VMManagement mgmt = (sun.management.VMManagement) jvm.get(runtime);
java.lang.reflect.Method pid_method = mgmt.getClass().getDeclaredMethod("getProcessId");
pid_method.setAccessible(true);
int pid = (Integer) pid_method.invoke(mgmt);
return pid;
}
private void runRealProgram(int ownPid) throws Exception {
String separator = System.getProperty("file.separator");
String classpath = System.getProperty("java.class.path");
String path = System.getProperty("java.home") + separator + "bin" + separator + "java";
ProcessBuilder processBuilder = new ProcessBuilder(path, "-cp", classpath, RealProgram.class.getName(), Integer.valueOf(ownPid).toString());
Process process = processBuilder.start();
process.waitFor();//will wait forever, but will close when the RealProgram closes
}
}
和 RealProgram 类:
package so_38154756;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.util.regex.Pattern;
public class RealProgram {
private Integer bodyguardPID;
private PrintWriter writer;
public RealProgram(Integer bodyguardPID, PrintWriter writer) {
this.bodyguardPID = bodyguardPID;
this.writer = writer;
}
public static void main(String[] args){
Integer sacrificialPID = Integer.parseInt(args[0]);
try {
PrintWriter writer = new PrintWriter(new File("output.txt"));
RealProgram rp = new RealProgram(sacrificialPID, writer);
writer.println("Past Constructor");
rp.checkToSeeIfBodyguardLives();
writer.println("Bodyguard had died");
writer.close();//yes, yes, this could be slightly prettier with Java 8's try-with-resources
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
private void checkToSeeIfBodyguardLives() {
while(true){
try { Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}//I wait because I was debugging - you may find it wise to not wait
//do whatever it is you're doing
if(!isBodyguardAlive()){
quit();
break;
}
}
}
private boolean isBodyguardAlive() {
try {
String process;
Pattern pattern = Pattern.compile(".*\\\""+bodyguardPID+"\\\".*");//Windows - have tested
//you'll have to figure out your own different pattern for linux - possible source for editing
writer.println("Searching for PID of "+bodyguardPID);
//Process p = Runtime.getRuntime().exec("ps -few"); - Linux - haven't tested - again, possible source for editing
Process p = Runtime.getRuntime().exec(System.getenv("windir") +"\\system32\\"+"tasklist.exe /fo csv /nh");//Windows - have tested
BufferedReader input = new BufferedReader(new InputStreamReader(p.getInputStream()));
while ((process = input.readLine()) != null) {
if(pattern.matcher(process).matches()){
return true;
}
}
input.close();
} catch (Exception err) {
err.printStackTrace();
}
return false;
}
private void quit(){
writer.println("Detected Bodyguard has died");
}
}
【讨论】: