JavaFX Concurrency Example
This is a JavaFX Concurrency Example. Java GUI applications are inherently multithreaded. Multiple threads perform different tasks to keep the UI in sync with the user actions. JavaFX, like Swing and AWT, uses a single thread, called JavaFXApplication Thread, to process all UI events.
The following table shows an overview of the whole article:
Table Of Contents
The following examples uses Java SE 8 and JavaFX 2.2.
1. Introduction
The nodes representing UI in aScene Graph are not thread-safe. Designing nodes that are not thread-safe has advantages and disadvantages. They are faster, as no synchronization is involved.
The disadvantage is that they need to be accessed from a single thread to avoid being in an illegal state. JavaFX puts a restriction that a liveScene Graph must be accessed from one and only one thread, the JavaFX ApplicationThread.
This restriction indirectly imposes another restriction that a UI event should not process a long-running task, as it will make the application unresponsive. The user will get the impression that the application is hung.
The following examples will show the problem in detail.
2. The given Problem
2.1 The Code
FxConcurrencyExample1.java
import javafx.application.Application;import javafx.event.ActionEvent;import javafx.event.EventHandler;import javafx.scene.Scene;import javafx.scene.control.Button;import javafx.scene.control.Label;import javafx.scene.control.TextArea;import javafx.scene.layout.HBox;import javafx.scene.layout.VBox;import javafx.stage.Stage;public class FxConcurrencyExample1 extends Application{// Create the TextAreaTextArea textArea = new TextArea();// Create the LabelLabel statusLabel = new Label("Not Started...");// Create the ButtonsButton startButton = new Button("Start");Button exitButton = new Button("Exit");public static void main(String[] args) {Application.launch(args);}@Overridepublic void start(final Stage stage) {// Create the Event-Handlers for the ButtonsstartButton.setOnAction(new EventHandler <ActionEvent>() { public void handle(ActionEvent event) { runTask(); } });exitButton.setOnAction(new EventHandler <ActionEvent>() { public void handle(ActionEvent event) { stage.close(); } });// Create the ButtonBoxHBox buttonBox = new HBox(5, startButton, exitButton);// Create the VBoxVBox root = new VBox(10, statusLabel, buttonBox, textArea);// Set the Style-properties of the VBoxroot.setStyle("-fx-padding: 10;" +"-fx-border-style: solid inside;" +"-fx-border-width: 2;" +"-fx-border-insets: 5;" +"-fx-border-radius: 5;" +"-fx-border-color: blue;");// Create the SceneScene scene = new Scene(root,400,300);// Add the scene to the Stagestage.setScene(scene);// Set the title of the Stagestage.setTitle("A simple Concurrency Example");// Display the Stagestage.show();}public void runTask() {for(int i = 1; i <= 10; i++) {try {String status = "Processing " + i + " of " + 10;statusLabel.setText(status);textArea.appendText(status+"\n");Thread.sleep(1000);}catch (InterruptedException e) {e.printStackTrace();}}}}The above program displays a window as shown in the GUI. It contains three controls.
- A Label to display the progress of a task
- A Start button to start the task
- An Exit button to exit the application
The program is very simple. When you click the StartButton, a task lasting for 10 seconds is started.
The logic for the task is in therunTask() method, which simply runs a loop ten times. Inside the loop, the task lets the current thread, which is the JavaFXApplication Thread, sleep for 1 second.
The program has two problems:
Click the StartButton and immediately try to click the ExitButton.
Clicking the ExitButton has no effect until the task finishes. Once you click the StartButton, you cannot do anything else on the window, except to wait for 10 seconds for the task to finish.
Inside the loop in therunTask() method, the program prints the status of the task on the standard output and displays the same in theLabel in the window. You don´t see the status updated in theLabel.
public void runTask() {for(int i = 1; i <= 10; i++) {try {String status = "Processing " + i + " of " + 10;statusLabel.setText(status);textArea.appendText(status+"\n");Thread.sleep(1000);}catch (InterruptedException e) {e.printStackTrace();}}}It is repeated to emphasize that all UI event handlers in JavaFX run on a single thread, which is the JavaFXApplicationThread. When the StartButton is clicked, therunTask() method is executed in the JavaFXApplicationThread.
When the ExitButton is clicked while the task is running, anActionEvent event for the ExitButton is generated and queued on the JavaFXApplicationThread. TheActionEvent handler for the ExitButton is run on the same thread after the thread is done running therunTask() method as part of theActionEvent handler for the StartButton.
A pulse event is generated when the Scene Graph is updated. The pulse event handler is also run on the JavaFXApplicationThread. Inside the loop, the text property of theLabel was updated ten times, which generated the pulse events. However, theScene Graph was not refreshed to show the latest text for theLabel, as the JavaFXApplicationThread was busy running the task and it did not run the pulse event handlers.
Both problems arise because there is only one thread to process all UI event handlers and you ran a long-running task in theActionEvent handler for the Start button.
What is the solution? You have only one option. You cannot change the single-threaded model for handling the UI events. You must not run long-running tasks in the event handlers.
Sometimes, it is a business need to process a big job as part of a user action. The solution is to run the long-running tasks in one or more background threads, instead of in the JavaFXApplicationThread.
2.2 The GUI
The following image shows the GUI after clicking the StartButton:

The following image shows the GUI of the above program after execution:

3. A first Solution Approach
3.1 The Code
FxConcurrencyExample2.java
import javafx.application.Application;import javafx.event.ActionEvent;import javafx.event.EventHandler;import javafx.scene.Scene;import javafx.scene.control.Button;import javafx.scene.control.Label;import javafx.scene.control.TextArea;import javafx.scene.layout.HBox;import javafx.scene.layout.VBox;import javafx.stage.Stage;public class FxConcurrencyExample2 extends Application{// Create the TextAreaTextArea textArea = new TextArea();// Create the LabelLabel statusLabel = new Label("Not Started...");// Create the ButtonsButton startButton = new Button("Start");Button exitButton = new Button("Exit");public static void main(String[] args) {Application.launch(args);}@Overridepublic void start(final Stage stage) {// Create the Event-Handlers for the ButtonsstartButton.setOnAction(new EventHandler <ActionEvent>() { public void handle(ActionEvent event) { startTask(); } });exitButton.setOnAction(new EventHandler <ActionEvent>() { public void handle(ActionEvent event) { stage.close(); } });// Create the ButtonBoxHBox buttonBox = new HBox(5, startButton, exitButton);// Create the VBoxVBox root = new VBox(10, statusLabel, buttonBox,textArea);// Set the Style-properties of the VBoxroot.setStyle("-fx-padding: 10;" +"-fx-border-style: solid inside;" +"-fx-border-width: 2;" +"-fx-border-insets: 5;" +"-fx-border-radius: 5;" +"-fx-border-color: blue;");// Create the SceneScene scene = new Scene(root,400,300);// Add the scene to the Stagestage.setScene(scene);// Set the title of the Stagestage.setTitle("A simple Concurrency Example");// Display the Stagestage.show();}public void startTask() {// Create a RunnableRunnable task = new Runnable(){public void run(){runTask();}};// Run the task in a background threadThread backgroundThread = new Thread(task);// Terminate the running thread if the application exitsbackgroundThread.setDaemon(true);// Start the threadbackgroundThread.start();}public void runTask() {for(int i = 1; i <= 10; i++) {try {String status = "Processing " + i + " of " + 10;statusLabel.setText(status);textArea.appendText(status+"\n");Thread.sleep(1000);}catch (InterruptedException e) {e.printStackTrace();}}}}The above program is your first, incorrect attempt to provide a solution. TheActionEvent handler for the StartButton calls thestartTask() method, which creates a new thread and runs therunTask() method in the new thread.
Run the program and click the StartButton. A runtime exception is thrown. The partial stack trace of the exception is as follows:
Exception in thread "Thread-4" java.lang.IllegalStateException: Not on FX application thread; currentThread = Thread-4at com.sun.javafx.tk.Toolkit.checkFxUserThread(Toolkit.java:236)at com.sun.javafx.tk.quantum.QuantumToolkit.checkFxUserThread(QuantumToolkit.java:423)at javafx.scene.Parent$2.onProposedChange(Parent.java:367)at com.sun.javafx.collections.VetoableListDecorator.setAll(VetoableListDecorator.java:113)at com.sun.javafx.collections.VetoableListDecorator.setAll(VetoableListDecorator.java:108)at com.sun.javafx.scene.control.skin.LabeledSkinBase.updateChildren(LabeledSkinBase.java:575)at com.sun.javafx.scene.control.skin.LabeledSkinBase.handleControlPropertyChanged(LabeledSkinBase.java:204)at com.sun.javafx.scene.control.skin.LabelSkin.handleControlPropertyChanged(LabelSkin.java:49)at com.sun.javafx.scene.control.skin.BehaviorSkinBase.lambda$registerChangeListener$61(BehaviorSkinBase.java:197)at com.sun.javafx.scene.control.MultiplePropertyChangeListenerHandler$1.changed(MultiplePropertyChangeListenerHandler.java:55)at javafx.beans.value.WeakChangeListener.changed(WeakChangeListener.java:89)at com.sun.javafx.binding.ExpressionHelper$SingleChange.fireValueChangedEvent(ExpressionHelper.java:182)at com.sun.javafx.binding.ExpressionHelper.fireValueChangedEvent(ExpressionHelper.java:81)at javafx.beans.property.StringPropertyBase.fireValueChangedEvent(StringPropertyBase.java:103)at javafx.beans.property.StringPropertyBase.markInvalid(StringPropertyBase.java:110)at javafx.beans.property.StringPropertyBase.set(StringPropertyBase.java:144)at javafx.beans.property.StringPropertyBase.set(StringPropertyBase.java:49)at javafx.beans.property.StringProperty.setValue(StringProperty.java:65)at javafx.scene.control.Labeled.setText(Labeled.java:145)at FXConcurrency.FxConcurrencyExample2.runTask(FxConcurrencyExample2.java:101)at FXConcurrency.FxConcurrencyExample2$3.run(FxConcurrencyExample2.java:82)at java.lang.Thread.run(Thread.java:745)
The following statement in therunTask() method generated the exception:

Thank you!
We will contact you soon.
statusLabel.setText(status);
The JavaFX runtime checks that a live scene must be accessed from the JavaFXApplicationThread.
TherunTask() method is run on a new thread, namedThread-4 as shown in the stack trace, which is not the JavaFXApplicationThread. The foregoing statement sets the text property for theLabel, which is part of a liveScene Graph, from the thread other than the JavaFXApplicationThread, which is not permissible.
How do you access a liveScene Graph from a thread other than the JavaFXApplicationThread?
The simple answer is that you cannot. The complex answer is that when a thread wants to access a liveScene Graph, it needs to run the part of the code that accesses theScene Graph in the JavaFXApplicationThread.
ThePlatform class in thejavafx.application package provides two static methods to work with the JavaFXApplicationThread.
- public static boolean isFxApplicationThread()
- public static void runLater(Runnable runnable)
TheisFxApplicationThread() method returns true if the thread calling this method is the JavaFXApplicationThread. Otherwise, it returns false.
TherunLater() method schedules the specifiedRunnable to be run on the JavaFXApplicationThread at some unspecified time in future.
3.2 The GUI
The following image shows the result of the program during execution:

4. The Solution
4.1 The Code
FxConcurrencyExample3.java
import javafx.application.Application;import javafx.application.Platform;import javafx.event.ActionEvent;import javafx.event.EventHandler;import javafx.scene.Scene;import javafx.scene.control.Button;import javafx.scene.control.Label;import javafx.scene.control.TextArea;import javafx.scene.layout.HBox;import javafx.scene.layout.VBox;import javafx.stage.Stage;public class FxConcurrencyExample3 extends Application{// Create the TextAreaTextArea textArea = new TextArea();// Create the LabelLabel statusLabel = new Label("Not Started...");// Create the ButtonsButton startButton = new Button("Start");Button exitButton = new Button("Exit");public static void main(String[] args) {Application.launch(args);}@Overridepublic void start(final Stage stage) {// Create the Event-Handlers for the ButtonsstartButton.setOnAction(new EventHandler <ActionEvent>() { public void handle(ActionEvent event) { startTask(); } });exitButton.setOnAction(new EventHandler <ActionEvent>() { public void handle(ActionEvent event) { stage.close(); } });// Create the ButtonBoxHBox buttonBox = new HBox(5, startButton, exitButton);// Create the VBoxVBox root = new VBox(10, statusLabel, buttonBox, textArea);// Set the Style-properties of the VBoxroot.setStyle("-fx-padding: 10;" +"-fx-border-style: solid inside;" +"-fx-border-width: 2;" +"-fx-border-insets: 5;" +"-fx-border-radius: 5;" +"-fx-border-color: blue;");// Create the SceneScene scene = new Scene(root,400,300);// Add the scene to the Stagestage.setScene(scene);// Set the title of the Stagestage.setTitle("A simple Concurrency Example");// Display the Stagestage.show();}public void startTask() {// Create a RunnableRunnable task = new Runnable(){public void run(){runTask();}};// Run the task in a background threadThread backgroundThread = new Thread(task);// Terminate the running thread if the application exitsbackgroundThread.setDaemon(true);// Start the threadbackgroundThread.start();}public void runTask() {for(int i = 1; i <= 10; i++) {try {// Get the Statusfinal String status = "Processing " + i + " of " + 10;// Update the Label on the JavaFx Application ThreadPlatform.runLater(new Runnable() { @Override public void run() { statusLabel.setText(status); } });textArea.appendText(status+"\n");Thread.sleep(1000);}catch (InterruptedException e) {e.printStackTrace();}}}}Let us fix the problem in the application. The above program is the correct implementation of the logic to access the liveScene Graph.
The program replaces the statement
statusLabel.setText(status);
in the Java class with the statement
// Update the Label on the JavaFx Application ThreadPlatform.runLater(new Runnable() { @Override public void run() {statusLabel.setText(status); }});Now, setting the text property for theLabel takes place on the JavaFXApplicationThread. TheActionEvent handler of the StartButton runs the task in a background thread, thus freeing up the JavaFXApplicationThread to handle user actions. The status of the task is updated in theLabel regularly. You can click the ExitButton while the task is being processed.
Did you overcome the restrictions imposed by the event-dispatching threading model of the JavaFX?
The answer is yes and no. You used a trivial example to demonstrate the problem. You have solved the trivial problem. However, in a real world, performing a long-running task in a GUI application is not so trivial.
For example, your task-running logic and the UI are tightly coupled as you are referencing theLabel inside therunTask() method, which is not desirable in a real world.
Your task does not return a result, nor does it have a reliable mechanism to handle errors that may occur. Your task cannot be reliably cancelled, restarted, or scheduled to be run at a future time.
TheJavaFX concurrency framework has answers to all these questions. The framework provides a reliable way of running a task in one or multiple background threads and publishing the status and the result of the task in a GUI application.
I will discuss the framework more in detail in a special article.
4.2 The GUI
The following GUI shows a snapshot of the window displayed by the program.

5. Download Java Source Code
This was an example ofjavafx.concurrent
You can download the full source code of this example here:JavaFxConcurrencyExample.zip

Thank you!
We will contact you soon.




