我想关闭一个带有指定返回码的javafx应用程序。浏览SO上的答案,我发现了以下成语:
Platform.exit();
System.exit(0);
例如:
Stop threads before close my JavaFX program
或者在这里:JavaFX application still running after close
这两个方法一个接一个地执行,看起来我们正在尝试复制某些操作。我认为,如果Platform.exit()
成功,它不应该返回到System.exit(0)
被调用的地方。但是,如果Platform.exit()
仅触发在另一个线程上运行的某些关闭操作,则可以调用return和System.exit(0)
,这可能会导致某些竞争条件,其中两个线程正在尝试关闭同一个应用程序。
那么,这个成语究竟是如何运作的?
答案 0 :(得分:11)
调用System.exit(...)
会终止Java虚拟机。
据我了解,调用Platform.exit()
只是表示JavaFX Toolkit已关闭,导致在FX Application线程上调用应用程序实例的stop()
方法,以及FX应用程序允许线程终止。这反过来导致Application.launch()
返回。如果您在main(...)
方法中使用通常的习语:
public static void main(String[] args) {
Application.launch(args);
}
然后一旦launch()
返回,main()
方法就没有任何东西可以做了,并且没有(只要没有运行非守护程序线程)应用程序以正常方式退出。 Platform.exit()
在任何情况下都不会创建对System.exit(...)
的调用:但是在某些情况下,它会允许JVM退出,因为它没有任何东西可以做。
如果您致电System.exit(...)
,JVM基本上会立即退出。因此,例如,如果您在main(...)
之后的Application.launch()
方法中有代码,那么该代码会在调用Platform.exit()
后执行,而不是在调用System.exit(...)
之后执行。同样,如果您覆盖Application.stop()
,则会在调用stop()
后调用Platform.exit()
方法,但在调用System.exit(...)
后则不会。
如果你有非守护程序线程在运行,Platform.exit()
不会强制关闭它们,但是System.exit()
会。
以下示例应说明这一点:
import javafx.application.Application;
import javafx.application.Platform;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.HBox;
import javafx.stage.Stage;
public class ExitTest extends Application {
@Override
public void stop() {
System.out.println("Stop called");
}
@Override
public void start(Stage primaryStage) {
Button startThread = new Button("Start non-daemon thread");
startThread.setOnAction(e -> new Thread(() -> {
System.out.println("Starting thread");
try {
Object lock = new Object();
synchronized(lock) {
lock.wait();
}
} catch (InterruptedException exc) {
System.err.println("Interrupted");
Thread.currentThread().interrupt();
} finally {
System.out.println("Thread complete");
}
}).start());
Button exit = new Button("Simple Exit");
exit.setOnAction(e -> {
System.out.println("Calling Platform.exit()");
Platform.exit();
});
Button forceExit = new Button("Force exit");
forceExit.setOnAction(e -> {
System.out.println("Calling Platform.exit():");
Platform.exit();
System.out.println("Calling System.exit(0):");
System.exit(0);
});
Scene scene = new Scene(new HBox(5, startThread, exit, forceExit));
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
System.out.println("launch() complete");
}
}
通常建议您通过调用Platform.exit()
退出JavaFX应用程序,这样可以正常关闭:例如,如果有任何"清理"您需要的代码,您可以将其放在stop()
方法中,Platform.exit()
将允许它执行。如果您正在运行必须终止的后台线程,请使它们成为守护程序线程,或者通过执行程序服务执行它们,并从stop()
方法关闭执行程序服务。以下是使用该技术的上述示例的修改。
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.HBox;
import javafx.stage.Stage;
public class ExitTest extends Application {
private final ExecutorService exec = Executors.newCachedThreadPool();
@Override
public void stop() throws InterruptedException {
System.out.println("Stop called: try to let background threads complete...");
exec.shutdown();
if (exec.awaitTermination(2, TimeUnit.SECONDS)) {
System.out.println("Background threads exited");
} else {
System.out.println("Background threads did not exit, trying to force termination (via interruption)");
exec.shutdownNow();
}
}
@Override
public void start(Stage primaryStage) {
Button startThread = new Button("Start non-daemon thread");
startThread.setOnAction(e -> {
exec.submit( () -> {
System.out.println("Starting thread");
try {
// just block indefinitely:
Object lock = new Object();
synchronized(lock) {
lock.wait();
}
} catch (InterruptedException exc) {
System.out.println("Interrupted");
Thread.currentThread().interrupt();
} finally {
System.out.println("Thread complete");
}
});
});
Button exit = new Button("Simple Exit");
exit.setOnAction(e -> {
System.out.println("Calling Platform.exit()");
Platform.exit();
});
Scene scene = new Scene(new HBox(5, startThread, exit));
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
System.out.println("launch() complete");
}
}
如果要使用Platform.exit()
以正常关闭,并且想要从System.exit(...)
返回值,则以下方法应该有效。请注意,这不是真正推荐的做法:在生产代码中,您根本不应该依赖支持流程退出代码的平台。
public class App extends Application {
private static int exitCode = 0 ;
public static exit(int exitCode) {
App.exitCode = exitCode ;
Platform.exit();
}
@Override
public void start(Stage primaryStage) {
// ...
someThing.addEventHander(someEventType, e -> App.exit(42));
// ...
}
@Override
public void stop() {
// cleanup code...
}
public static void main(String[] args) {
Application.launch(args);
System.exit(exitCode);
}
}
答案 1 :(得分:3)
调用System.exit(0)
将终止JVM
终止当前运行的Java虚拟机。争论 作为状态代码;按照惯例,非零状态代码 表示异常终止。此方法调用exit方法 class Runtime。此方法永远不会正常返回。
并且Platform.exit()
将终止FX应用程序
在我看来,两者都是杀死应用程序的糟糕方法......导致JavaFX应用程序终止。如果调用此方法 在调用Application start方法之后,再调用JavaFX启动器 将调用Application stop方法并终止JavaFX 应用程序线程然后启动程序线程将关闭。如果有 没有其他正在运行的非守护程序线程,Java VM将会运行 出口。如果从Preloader或Application调用此方法 init方法,然后可能不会调用Application stop方法。
答案 2 :(得分:1)
另一种避免黑客攻击的方法是检查是否打开了任何阶段,并使用Stage.close()关闭它们,然后 then 调用Platform.exit()。例如,在两个窗口的应用程序中:
if (secondWindow != null) secondWindow.close();
Platform.exit();