I am trying to understand how the JavaFX threads work. Descriptions here are not helping much.
For example in code below, the order of printing is always A
then B
then Z
, which suggests that both the load()
call and the code inside changed()
are running on the same thread.
But I do not understand, why will the thread not just exit after the last Thread.sleep(2000)
(since there is no more work to be done)?
I would expect the code inside changed()
to run on a new thread everytime, and am utterly confused as to how this works!
public class Test extends Application {
@Override
public void start(Stage stage) throws Exception {
final WebView webView = new WebView();
Scene scene = new Scene(webView);
stage.setScene(scene);
stage.show();
webView.getEngine().getLoadWorker().stateProperty()
.addListener(new ChangeListener<State>() {
@Override
public void changed(ObservableValue<? extends State> ov, State t, State t1) {
if (t1 == Worker.State.SUCCEEDED) {
System.out.println("Z"); // <---
}
}
});
webView.getEngine().load("http://www.google.com");
System.out.println("A"); // <---
Thread.sleep(2000);
System.out.println("B"); // <---
Thread.sleep(2000);
}
public static void main(String[] args) {
launch(args);
}
}
start()
and changed()
run from the "JavaFX Application Thread". This thread is responsible for monitoring the UI for events, dispatching them to user code and repainting the UI. Unlike Runnable.run()
that when exits, the thread terminates, start()
is called by the "JavaFX Application Thread" in something like a loop: when start()
ends, the next iteration is executed. (Actually the iterations run asynchronously/wait for events, so as not to eat all CPU resources, but the loop is OK as a simplified model.)
I'll try to explain in a little more detail.
Place print statements as (unindented to stand out):
public void start(Stage stage) throws Exception {
System.out.println("2: " + Thread.currentThread().getName());
...
webView.getEngine().getLoadWorker().stateProperty().addListener(new ChangeListener<State>() {
@Override
public void changed(ObservableValue<? extends State> ov, State t, State t1) {
if (t1 == Worker.State.SUCCEEDED) {
System.out.println("3: " + Thread.currentThread().getName());
System.out.println("Z"); // <---
}
}
});
webView.getEngine().load("...");
System.out.println("A"); // <---
Thread.sleep(2000);
System.out.println("B"); // <---
Thread.sleep(2000);
}
public static void main(String[] args) {
System.out.println("1: " + Thread.currentThread().getName());
launch(args);
System.out.println("4: " + Thread.currentThread().getName());
}
Observe the console and the application:
User action: Launch the app
Console: The following comes out immediately:
1: main
2: JavaFX Application Thread
Application: The main window appears... however without the content! Why? Because the "JavaFX Application Thread", that is responsible for painting the UI, is sleep()
-ing, before point "A".
Console: "A" is printed after 2 seconds
Application: Still sleeping for "B", still no content in the window
Console: "B" is printed after 2 seconds
Application: Things come to life, the content starts filling; the UI thread is no longer blocked in sleep()
so it is free to do its work.
Notice that despite the fact that start()
is over, launch()
is NOT, so the main thread is alive and well, thus the application is active. After a while, the page is loaded:
Console:
3: JavaFX Application Thread
Z
User action: Press close
Console:
4: main
Application: The window closes, the application exits
To gain more insight in this topic you can place breakpoints in main()
and start()
and observe the various threads that are active.
Comment: The observed behaviour from the sleep()
calls in the UI thread is why you must never perform long-running tasks inside the UI thread, if you want the application to be responsive.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With