Welcome to ShenZhenJia Knowledge Sharing Community for programmer and developer-Open, Learning and Share
menu search
person
Welcome To Ask or Share your Answers For Others

Categories

I would like to close a javafx application with a specified return code. Browsing through answers on SO, I found the following idiom:

Platform.exit();
System.exit(0); 

for example here: Stop threads before close my JavaFX program
or here: JavaFX application still running after close

These two methods executed one after another look like we are attempting to duplicate some actions. I would assume, that if Platform.exit() is successful, it should not return to the place where System.exit(0) is called. If however Platform.exit() only triggers some closing action running on another thread, returns and System.exit(0) can be called then this may cause some race condition, where two threads are trying to close the same application.

So, how does this idiom exactly work?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
thumb_up_alt 0 like thumb_down_alt 0 dislike
111 views
Welcome To Ask or Share your Answers For Others

1 Answer

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);
    }
}

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
thumb_up_alt 0 like thumb_down_alt 0 dislike
Welcome to ShenZhenJia Knowledge Sharing Community for programmer and developer-Open, Learning and Share
...