How do I start one thread for my code and one for a JavaFX Application?

前端 未结 2 812
清酒与你
清酒与你 2021-01-21 23:50

I\'m trying to run a program using JavaFX. If I was using Swing, I would have one class started by the main method, and have it build the GUI class. That would give me 2 threads

2条回答
  •  南笙
    南笙 (楼主)
    2021-01-22 00:36

    In JavaFX, I'm guessing in order to prevent the very common threading errors that were present in many Swing applications, the startup process is constrained so that (more or less) the only way to do things forces you to execute the UI code on the FX Application Thread:

    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....
        }
    
    }
    

    In an Oracle JRE, executing java MyAppStartClass will (in contrast to a regular Java application), cause an instance of MyAppStartClass to be created, the FX Application Thread to be started, and the start method of the created instance to be executed on the FX Application Thread. (There is a bit more to it than that, but that's the basic gist.)

    If you want to support environments that aren't aware of how to execute a JavaFX Application (including many IDEs), you can add a main method that forces this to happen, simply by calling the static Application.launch() method:

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

    Note there's an overloaded form of launch, where you can specify an Application subclass, so you could have a different main class (the use cases for this are very few):

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

    There are two important features of the launch method to be aware of:

    1. It can only be called once per JVM lifetime. Calling it a second time will throw an exception.
    2. It will block until the JavaFX platform exits.

    If you want to have a thread doing work besides the FX Application Thread, the simplest way is to start it directly from the start method:

    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...
        }
    
    }
    

    If you compare this to the standard startup for a Swing application:

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

    the process is kind of inverted compared to Swing. In Swing you (are supposed to) explicitly have the startup method (main) specify that the UI is run on the AWT event dispatch thread, and you can do other stuff in the "current" thread in which main is executed. In JavaFX the startup method (start) is executed on the UI thread, and if you want to do stuff on another thread you explicitly launch one.

    Other rules are pretty much the same: UI elements that are part of the scene graph can only be modified on the UI thread, etc. Note JavaFX has a specific concurrency API for managing tasks in a background thread and scheduling UI updates on the FX Application Thread.

    Aside:

    In theory I suppose you could do something like:

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

    But since that idiom is far from the usual setup, I would not recommend it. Note especially you should not have a main method like this directly in the Application subclass, as (I think) an Oracle JRE (or an environment that knows how to run JavaFX Applications) might ignore the main method and just boot up your start method as described at the top of this post.

提交回复
热议问题