我想用指定的返回码关闭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);
    }
}

07-26 09:29
查看更多