如何为我的代码启动一个线程,为JavaFX应用程序启动一个线程?

我正在尝试使用JavaFX运行程序。 如果我使用Swing,我将使用main方法启动一个类,并让它构建GUI类。 这将给我2个线程,一个应用程序的正常线程和EventQueue。 这样可以防止阻止UI工作。

所以,我试着让在main main方法中创建的类构造Application类,然后启动它。 我得到了一个RuntimeException,因为调用launch方法的程序不是Application的子类。

有没有办法分离线程,或者一切都必须在给予Application类的线程内工作?

在JavaFX中,我猜测为了防止许多Swing应用程序中出现的非常常见的线程错误,启动过程受到约束,因此(或多或少)执行操作的唯一方法会强制您执行UI代码外汇申请线程:

public class MyAppStartClass extends Application { @Override public void start(Stage primaryStage) { // this method will be executed on the FX Application Thread // load UI and display it here.... } } 

在Oracle JRE中,执行java MyAppStartClass (与常规Java应用程序相反)将导致创建MyAppStartClass实例, MyAppStartClass FX Application Thread,以及在FX上执行创建实例的start方法应用线程。 (还有更多的东西,但这是基本的要点。)

如果您想支持不了解如何执行JavaFX应用程序(包括许多IDE)的环境,您可以添加一个强制执行此操作的main方法,只需调用静态Application.launch()方法:

 public class MyAppStartClass extends Application { @Override public void start(Stage primaryStage) { // this method will be executed on the FX Application Thread // load UI and display it here.... } // to support non-JavaFX-aware environments: public static void main(String[] args) { launch(args); } } 

注意有一个重载forms的launch ,你可以在其中指定一个Application子类,所以你可以有一个不同的主类(用例很少):

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

launch方法有两个重要特征需要注意:

  1. 每个JVM生命周期只能调用一次。 再次调用它将引发exception。
  2. 它会阻塞,直到JavaFX平台退出。

如果你想让一个线程在FX Application Thread之外工作,最简单的方法是直接从start方法启动它:

 public class MyAppStartClass extends Application { @Override public void start(Stage primaryStage) { // start a background thread to do background stuff: new Thread(() -> { // background work... }).start(); // UI work... } } 

如果将此与Swing应用程序的标准启动进行比较:

 public class MySwingApp { public static void main(String[] args) { SwingUtilities.invokeLater(() -> { // UI work... }); // background work... } } 

与Swing相比,这个过程有点颠倒。 在Swing中,您(应该)明确地让启动方法(main)指定UI在AWT事件派发线程上运行,并且您可以在执行main的“当前”线程中执行其他操作。 在JavaFX中,启动方法(start)在UI线程上执行,如果你想在另一个线程上执行操作,则显式启动一个。

其他规则几乎相同:作为场景图的一部分的UI元素只能在UI线程等上修改。注意JavaFX有一个特定的并发API,用于管理后台线程中的任务和调度FX应用程序上的UI更新线。

在旁边:

理论上我认为你可以这样做:

 public class MainClass { public static void main(String[] args) { new Thread(() -> Application.launch(MyAppStartClass.class, args)).start(); // do "background thread" work here in the (now free) main thread } } 

但由于这个成语远非通常的设置,我不推荐它。 特别要注意,你不应该在Application子类中直接使用这样的main方法,因为(我认为)Oracle JRE(或者知道如何运行JavaFX应用程序的环境)可能会忽略main方法,只需将start方法启动为在这篇文章的顶部描述。

它与Swing并没有什么不同,FX和Swing都使用自己的Event调度线程。

以这个例子运行3个应用程序:

 import java.util.Scanner; import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.SwingUtilities; import javafx.application.Application; import javafx.scene.Scene; import javafx.scene.control.Button; import javafx.scene.layout.StackPane; import javafx.stage.Stage; public class TripleFun { public static class CmdApplication { public void doThings() { Scanner in = new Scanner(System.in); while (true) { String line = in.nextLine(); if ("quit".equals(line)) return; println("Hello: " + line); } } } public static class SwingApplication extends JFrame { public SwingApplication() { setTitle("Swing"); setSize(200, 200); setVisible(true); JButton button = new JButton("Ok."); button.addActionListener(e -> println("Swing button")); add(button); setDefaultCloseOperation(DISPOSE_ON_CLOSE); println("Swing Thread"); } } public static class FxApplication extends Application { @Override public void start(Stage primaryStage) throws Exception { println("Fx Thread"); StackPane root = new StackPane(); Button button = new Button(); button.setText("Ok."); button.setOnAction(e -> println("Fx button")); root.getChildren().add(button); Scene scene = new Scene(root, 300, 250); primaryStage.setTitle("FX"); primaryStage.setScene(scene); primaryStage.show(); } } public static void main(String[] args) { SwingUtilities.invokeLater(() -> { new SwingApplication(); println("swing invoke done."); }); println("Started Swing."); new Thread(() -> { println("FX launch thread started."); Application.launch(FxApplication.class, args); println("FX launch thread done."); }).start(); println("Started Fx."); new Thread(() -> { println("Cmd thread started."); new CmdApplication().doThings(); println("Cmd thread done."); }).start(); println("Started cmd. initial thread done."); } public static void println(String msg) { System.out.println(Thread.currentThread().getName() + " - " + msg); } } 

有一些输出像

 main - Started Swing. main - Started Fx. Thread-1 - FX launch thread started. main - Started cmd. initial thread done. Thread-2 - Cmd thread started. AWT-EventQueue-0 - Swing Thread AWT-EventQueue-0 - swing invoke done. JavaFX Application Thread - Fx Thread AWT-EventQueue-0 - Swing button AWT-EventQueue-0 - Swing button JavaFX Application Thread - Fx button JavaFX Application Thread - Fx button ok Thread-2 - Hello: ok ok Thread-2 - Hello: ok quit Thread-2 - Cmd thread done. Thread-1 - FX launch thread done. 

具有main方法的初始线程几乎立即结束,每个子应用程序继续在自己的线程上运行。 所有线程完成后,应用程序作为一个整体结束。

关于JavaFX唯一棘手的问题是它的launch方法阻塞,直到应用程序(在它自己的线程中运行)完成。 您可以让它接管初始main线程并在新线程中运行其他代码,或者将启动移动到新线程以保持主线程空闲。