我想用指定的返回码关闭javafx应用程序。浏览关于SO的答案,我发现以下成语:
Platform.exit();
System.exit(0);
例如这里:
Stop threads before close my JavaFX program
或这里:JavaFX application still running after close
这两个方法一个接一个地执行,好像我们试图重复一些 Action 。我假设,如果
Platform.exit()
成功,则不应返回到调用System.exit(0)
的地方。但是,如果Platform.exit()
仅触发在另一个线程上运行的某些关闭操作,返回并可以调用System.exit(0)
,则这可能会导致某些竞争状况,其中两个线程试图关闭同一应用程序。那么,这个习惯用法到底如何工作?
最佳答案
调用System.exit(...)
将终止Java虚拟机。
据我了解,调用Platform.exit()
只是发出信号通知JavaFX Toolkit关闭,从而导致在FX Application线程上调用应用程序实例的stop()
方法,并且允许FX Application Thread终止。依次导致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);
}
}