Calling System.exit(...)
terminates the Java Virtual Machine.
As I understand it, calling Platform.exit()
just signals the JavaFX Toolkit to shut down, resulting in the application instance's stop()
method being called on the FX Application thread, and the FX Application Thread being allowed to terminate. This in turn causes Application.launch()
to return. If you are using the usual idiom in your main(...)
method:
public static void main(String[] args) {
Application.launch(args);
}
then once launch()
returns, there is nothing left for the main()
method to do, and no (as long as no non-daemon threads are running) the application exits in a normal way. Platform.exit()
does not create a call to System.exit(...)
under any circumstances: however under certain circumstances it will allow the JVM to exit simply because there is nothing left for it to do.
If you call System.exit(...)
the JVM basically exits immediately. So, for example, if you have code in the main(...)
method after Application.launch()
, that code gets executed after a call to Platform.exit()
, but not after a call to System.exit(...)
. Similarly, if you override Application.stop()
, the stop()
method is called after a call to Platform.exit()
, but not after a call to System.exit(...)
.
If you have non-daemon threads running, Platform.exit()
will not forcibly shut them down, but System.exit()
will.
The following example should demonstrate this:
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");
}
}
It's generally recommended that you exit a JavaFX Application with a call to Platform.exit()
, which allows for a graceful shutdown: for example if there is any "cleanup" code you need, you can put it in the stop()
method and Platform.exit()
will allow it to be executed. If you are running background threads which must be terminated, either make them daemon threads, or execute them via an executor service, and shut down the executor service from the stop()
method. Here is a modification to the above example which uses this technique.
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");
}
}
If you want to use Platform.exit()
in order to have a graceful shutdown, and you want to return a value from System.exit(...)
, the following approach should work. Note that this is not really a recommended practice anyway: in production code you should not really rely on the platform supporting a process exit code at all.
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);
}
}