Frage Verwenden von Platform.exit () und System.exit (int) zusammen


Ich möchte eine Javafx-Anwendung mit einem angegebenen Rückgabecode schließen. Beim Durchsuchen der Antworten auf SO habe ich folgendes Idiom gefunden:

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

zum Beispiel hier: Stoppen Sie die Threads, bevor Sie mein JavaFX-Programm schließen 
oder hier: JavaFX-Anwendung läuft noch nach dem Schließen

Diese beiden Methoden, die nacheinander ausgeführt werden, sehen so aus, als würden wir versuchen, einige Aktionen zu duplizieren. Ich würde annehmen, dass wenn Platform.exit() ist erfolgreich, sollte es nicht an den Ort zurückkehren, wo System.exit(0) wird genannt. Wenn jedoch Platform.exit() löst nur eine Abschlussaktion aus, die auf einem anderen Thread ausgeführt wird, gibt und zurück System.exit(0) kann aufgerufen werden, dann kann dies eine Race-Bedingung verursachen, wo zwei Threads versuchen, die gleiche Anwendung zu schließen.

Also, wie funktioniert dieses Idiom genau?


5
2017-09-05 11:32


Ursprung


Antworten:


Berufung System.exit(...) beendet die Java Virtual Machine.

Wie ich es verstehe, rufen Platform.exit() Sagt dem JavaFX-Toolkit nur an, dass es heruntergefahren wird, was zu der Anwendungsinstanz führt stop() Methode, die für den FX-Anwendungsthread aufgerufen wird und der FX-Anwendungsthread beendet werden darf. Dies wiederum verursacht Application.launch() zurückgeben. Wenn Sie das übliche Idiom in Ihrem verwenden main(...) Methode:

public static void main(String[] args) {
    Application.launch(args);
}

dann einmal launch() zurück, da ist nichts mehr für die main() method to do und no (solange keine nicht-Daemon-Threads ausgeführt werden) wird die Anwendung auf normale Weise beendet. Platform.exit() erstellt keinen Anruf an System.exit(...) unter keinen Umständen jedoch: unter bestimmten Umständen wird es der JVM möglich sein, das Land zu verlassen, weil es nichts mehr zu tun gibt.

Wenn du anrufst System.exit(...) die JVM verlässt im Grunde sofort. Also zum Beispiel, wenn Sie Code in der main(...) Methode nach Application.launch(), dieser Code wird nach einem Aufruf von ausgeführt Platform.exit(), aber nicht nach einem Anruf an System.exit(...). In ähnlicher Weise, wenn Sie überschreiben Application.stop(), das stop() Methode wird nach einem Aufruf an aufgerufen Platform.exit(), aber nicht nach einem Anruf an System.exit(...).

Wenn Sie Nicht-Daemon-Threads ausführen, Platform.exit() werde sie nicht gewaltsam schließen, aber System.exit() werden.

Das folgende Beispiel soll dies demonstrieren:

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

Im Allgemeinen wird empfohlen, eine JavaFX-Anwendung mit einem Aufruf von zu beenden Platform.exit(), die ein ordnungsgemäßes Herunterfahren ermöglicht: Wenn zum Beispiel ein "Bereinigungs" -Code benötigt wird, können Sie ihn in die stop() Methode und Platform.exit() wird erlauben, dass es ausgeführt wird. Wenn Sie Hintergrundthreads ausführen, die beendet werden müssen, erstellen Sie ihnen entweder Daemon-Threads, oder führen Sie sie über einen Executor-Dienst aus, und fahren Sie den Executor-Dienst über die stop() Methode. Hier ist eine Modifikation des obigen Beispiels, die diese Technik verwendet.

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

Wenn Sie verwenden möchten Platform.exit() um ein ordnungsgemäßes Herunterfahren zu haben und von dem Sie einen Wert zurückgeben möchten System.exit(...)sollte der folgende Ansatz funktionieren. Beachten Sie, dass dies ohnehin nicht empfohlen wird: Im Produktionscode sollten Sie sich nicht wirklich darauf verlassen, dass die Plattform überhaupt einen Prozessbeendigungscode unterstützt.

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

9
2017-09-05 17:14



Nun, der Doc ist dein Freund in einem solchen Fall:

Berufung System.exit(0) beendet die JVM

Beendet die aktuell laufende Java Virtual Machine. Das Argument   dient als Statuscode; per Konvention ein Statuscode ungleich Null   zeigt abnormale Beendigung an. Diese Methode ruft die Exit-Methode auf   Klasse Laufzeit. Diese Methode wird nie normal zurückgegeben.

und machen Platform.exit() beendet die FX-Anwendung

Bewirkt, dass die JavaFX-Anwendung beendet wird. Wenn diese Methode aufgerufen wird   Nachdem die Application-Start-Methode aufgerufen wurde, wird der JavaFX-Launcher gestartet   ruft die Methode Application stop auf und beendet JavaFX   Anwendungsthread. Der Launcher-Thread wird dann heruntergefahren. Wenn da   Es gibt keine anderen Nicht-Daemon-Threads, die ausgeführt werden, die Java VM   Ausfahrt. Wenn diese Methode vom Preloader oder von der Anwendung aufgerufen wird   Init-Methode, dann kann die Application-Stop-Methode nicht aufgerufen werden.


Beide sind meiner Meinung nach schlechte Ansätze, um die Anwendung zu töten ...


2
2017-09-05 11:40