一起使用 Platform.exit() 和 System.exit(int)
Posted
技术标签:
【中文标题】一起使用 Platform.exit() 和 System.exit(int)【英文标题】:Using Platform.exit() and System.exit(int) together 【发布时间】:2018-02-13 16:34:35 【问题描述】:我想关闭带有指定返回码的 javafx 应用程序。浏览SO上的答案,我发现了以下成语:
Platform.exit();
System.exit(0);
例如这里: Stop threads before close my JavaFX program 或在这里:JavaFX application still running after close
这两个方法一个接一个地执行,看起来我们在尝试复制一些动作。我会假设,如果Platform.exit()
成功,它不应该返回到调用System.exit(0)
的地方。但是,如果Platform.exit()
仅触发在另一个线程上运行的某些关闭操作,返回并且可以调用System.exit(0)
,那么这可能会导致一些竞争条件,其中两个线程试图关闭同一个应用程序。
那么,这个成语究竟是如何工作的呢?
【问题讨论】:
【参考方案1】:调用System.exit(...)
会终止Java 虚拟机。
据我了解,调用 Platform.exit()
只是指示 JavaFX Toolkit 关闭,导致在 FX 应用程序线程上调用应用程序实例的 stop()
方法,并允许 FX 应用程序线程终止。这反过来又导致Application.launch()
返回。如果您在 main(...)
方法中使用惯用语:
public static void main(String[] args)
Application.launch(args);
然后一旦launch()
返回,main()
方法就没有什么可做的了,并且没有(只要没有非守护线程正在运行)应用程序以正常方式退出。 Platform.exit()
在任何情况下都不会创建对 System.exit(...)
的调用:但是在某些情况下,它会允许 JVM 退出,因为它无事可做。
如果你调用System.exit(...)
,JVM 基本上会立即退出。因此,例如,如果您在Application.launch()
之后的main(...)
方法中有代码,则该代码将在调用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);
【讨论】:
感谢您的解释。我想知道您在生产代码中写了关于“不推荐的做法”的内容。这是否意味着生产代码不应该返回除 0 之外的任何其他退出代码? @PiotrG 你不应该依赖退出代码被传播回给启动 JVM 的人,因为它实际上是一个依赖于平台的特性。 嗯 ...如果您知道您的生产应用程序只会在(比如说)Linux 上运行,那么假设类似 Linux 的退出代码处理并非不合理。【参考方案2】:在这种情况下,Doc 是你的朋友:
调用System.exit(0)
将终止JVM
终止当前运行的 Java 虚拟机。论据 作为状态码;按照惯例,一个非零状态码 表示异常终止。该方法调用中的exit方法 类运行时。此方法永远不会正常返回。
Platform.exit()
将终止 FX 应用程序
导致 JavaFX 应用程序终止。如果调用此方法 调用 Application start 方法后,JavaFX 启动器 将调用 Application stop 方法并终止 JavaFX 应用程序线程。然后启动器线程将关闭。如果有 没有其他正在运行的非守护线程,Java VM 将 出口。如果从 Preloader 或 Application 调用此方法 init 方法,则应用程序停止方法可能不会被调用。
【讨论】:
嗯,你引用的内容并没有回答我的问题。在非守护线程的情况下运行Platform.exit()
应该继续关闭JVM,那么它如何返回到调用System.exit(0)
的地方呢?如果它返回,那么这两种方法都会竞相终止 JVM。
对不起,我没有完全关注你。 “两句”是指“第二句”,即:“调用 System.exit(0) 将终止 JVM”?是的,System.exit(0) 会终止 JVM,但首先调用 Platform.exit() 也会导致退出 JVM。而且我仍然不知道Platform.exit()如何返回到System.exit()被调用的地方。
最后一行加一个。有正确的方法可以做到这一点;然后是黑客。这些都是黑客。
好的,那么关闭返回特定状态代码的 JavaFX 应用程序的正确方法是什么?
@BoristheSpider Platform.exit()
是黑客吗?它允许正常关闭,让 FX 应用程序线程完成,并调用 stop()
回调方法(假设应用程序具有 start()
ed),从而允许清理资源。【参考方案3】:
另一种避免黑客攻击的方法是检查是否有任何阶段打开并使用 Stage.close() 关闭它们,然后然后调用 Platform.exit()。例如,在一个有两个窗口的应用程序中:
if (secondWindow != null) secondWindow.close();
Platform.exit();
【讨论】:
以上是关于一起使用 Platform.exit() 和 System.exit(int)的主要内容,如果未能解决你的问题,请参考以下文章