JavaFX

The JavaFX Concurrent Framework

Photo of Andreas PomarolliAndreas PomarolliJuly 28th, 2016Last Updated: April 24th, 2019
2 1,083 18 minutes read

This is an article about the JavaFX Concurrent Framework API. Java 5 added a comprehensive concurrency framework to the Java programming language through the libraries in thejava.util.concurrent package. The JavaFX Concurrency Framework is very small.

It is built on top of the Java language Concurrency Framework keeping in mind that it will be used in a GUI environment.
 
 
 
 
 
 

 
The following table shows an overview of the whole article:

The following examples use Java SE 8 and JavaFX 2.2.

1. Introduction

The framework consists of one interface, four classes, and one enum.

An instance of theWorker interface represents aTask that needs to be performed in one or more background threads. The state of theTask is observable from the JavaFX ApplicationThread.

TheTask,Service, andScheduledService classes implement theWorker interface. They represent different types of tasks. They are abstract classes. An instance of theTask class represents a one-shot task.

ATask cannot be reused. An instance of theService class represents a reusable task. TheScheduledService class inherits from theService class. AScheduledService is aTask that can be scheduled to run repeatedly after a specified interval.

The constants in theWorker.State enum represent different states of aWorker.

An instance of theWorkerStateEvent class represents an event that occurs as the state of aWorker changes. You can add event handlers to all three types of tasks to listen to the change in their states.

2. Understanding the Worker Interface

TheWorker<V> interface provides the specification for any task performed by the JavaFX Concurrency Framework. AWorker is aTask that is performed in one or more background threads. The generic parameterV is the data type of the result of theWorker.

The state of theTask is observable. The state of theTask is published on the JavaFXApplicationThread, making it possible for theTask to communicate with theScene Graph, as is commonly required in a GUI application.

2.1 Utility Classes

Let us create the reusable GUI and non-GUI parts of the programs to use in examples in the following sections.

TheWorkerStateGUI class builds aGridPane to display all properties of aWorker.

It is used with aWorker<ObservableList<Long>>. It displays the properties of aWorker by UI elements to them. You can bind properties of aWorker to the UI elements by passing a Worker to the constructor or calling thebindToWorker() method.

WorkerStateGUI.java

import javafx.beans.binding.When;import javafx.collections.ObservableList;import javafx.concurrent.Worker;import javafx.scene.control.Label;import javafx.scene.control.ProgressBar;import javafx.scene.control.TextArea;import javafx.scene.layout.GridPane;import javafx.scene.layout.HBox;import javafx.beans.value.ChangeListener;import javafx.beans.value.ObservableValue;public class WorkerStateGUI extends GridPane{// Create the Labelsprivate final Label title = new Label("");private final Label message = new Label("");private final Label running = new Label("");private final Label state = new Label("");private final Label totalWork = new Label("");private final Label workDone = new Label("");private final Label progress = new Label("");// Create the TextAreasprivate final TextArea value = new TextArea("");private final TextArea exception = new TextArea("");// Create the ProgressBarprivate final ProgressBar progressBar = new ProgressBar();public WorkerStateGUI() {addGUI();}public WorkerStateGUI(Worker<ObservableList<Long>> worker) {addGUI();bindToWorker(worker);}private void addGUI() {value.setPrefColumnCount(20);value.setPrefRowCount(3);exception.setPrefColumnCount(20);exception.setPrefRowCount(3);this.setHgap(5);this.setVgap(5);addRow(0, new Label("Title:"), title);addRow(1, new Label("Message:"), message);addRow(2, new Label("Running:"), running);addRow(3, new Label("State:"), state);addRow(4, new Label("Total Work:"), totalWork);addRow(5, new Label("Work Done:"), workDone);addRow(6, new Label("Progress:"), new HBox(2, progressBar, progress));addRow(7, new Label("Value:"), value);addRow(8, new Label("Exception:"), exception);}public void bindToWorker(final Worker<ObservableList<Long>> worker) {// Bind Labels to the properties of the workertitle.textProperty().bind(worker.titleProperty());message.textProperty().bind(worker.messageProperty());running.textProperty().bind(worker.runningProperty().asString());state.textProperty().bind(worker.stateProperty().asString());totalWork.textProperty().bind(new When(worker.totalWorkProperty().isEqualTo(-1)).then("Unknown").otherwise(worker.totalWorkProperty().asString()));workDone.textProperty().bind(new When(worker.workDoneProperty().isEqualTo(-1)).then("Unknown").otherwise(worker.workDoneProperty().asString()));progress.textProperty().bind(new When(worker.progressProperty().isEqualTo(-1)).then("Unknown").otherwise(worker.progressProperty().multiply(100.0).asString("%.2f%%")));progressBar.progressProperty().bind(worker.progressProperty());value.textProperty().bind(worker.valueProperty().asString());worker.exceptionProperty().addListener(new ChangeListener<Throwable>(){public void changed(ObservableValue<? extends Throwable> prop,            final Throwable oldValue, final Throwable newValue){if (newValue != null) {exception.setText(newValue.getMessage());} else {exception.setText("");}}});}}

ThePrimeUtil class is a utility class to check whether a number is a prime number.

PrimeUtil.java

public class PrimeUtil {public static boolean isPrime(long num) {if (num <= 1 || num % 2 == 0) {return false;}int upperDivisor = (int)Math.ceil(Math.sqrt(num));for (int divisor = 3; divisor <= upperDivisor; divisor += 2) {if (num % divisor == 0) {return false;}}return true;}}

2.2 State Transitions for a Worker

During the life cycle, aWorker transitions through different states. The constants in theWorker.State enum represent the valid states of aWorker.

  • Worker.State.READY
  • Worker.State.SCHEDULED
  • Worker.State.RUNNING
  • Worker.State.SUCCEEDED
  • Worker.State.CANCELLED
  • Worker.State.FAILED

When aWorker is created, it is in theREADY state. It transitions to theSCHEDULED state, before it starts executing. When it starts running, it is in theRUNNING state. Upon successful completion, aWorker transitions from theRUNNING state to theSUCCEEDED state. If theWorker throws an exception during its execution, it transitions to theFAILED state. AWorker may be cancelled using thecancel() method.

It may transition to theCANCELLED state from theREADY,SCHEDULED, andRUNNING states. These are the normal state transitions for a one-shotWorker.

A reusableWorker may transition from theCANCELLED,SUCCEEDED, andFAILED states to theREADY state.

2.3 Properties of a Worker

TheWorker interface contains nine read-only properties that represent the internal state of theTask.

  • title
  • message
  • running
  • state
  • progress
  • workDone
  • totalWork
  • value
  • exception

When you create aWorker, you will have a chance to specify these properties. The properties can also be updated as the task progresses.

Thetitle property represents the title for the task.

Themessage property represents a detailed message during the task processing.

Therunning property tells whether theWorker is running. It is true when the Worker is in theSCHEDULED orRUNNING states. Otherwise, it is false.

Thestate property specifies the state of theWorker. Its value is one of the constants of theWorker.State enum.

ThetotalWork,workDone, andprogress properties represent the progress of the task. ThetotalWork is the total amount of work to be done. TheworkDone is the amount of work that has been done. Theprogress is the ratio ofworkDone andtotalWork.

Thevalue property represents the result of the task. Its value is non-null only when theWorker finishes successfully reaching theSUCCEEDED state.

A task may fail by throwing an exception. The exception property represents the exception that is thrown during the processing of the task. It is non-null only when the state of theWorker isFAILED.

Typically, when aTask is in progress, you want to display the task details in aScene Graph.

The Concurrency Framework makes sure that the properties of aWorker are updated on the JavaFXApplicationThread. Therefore, it is fine to bind the properties of the UI elements in a Scene Graph to these properties.

3. Using the Task Class

An instance of theTask<V> class represents a one-time task. Once the task is completed, cancelled, or failed, it cannot be restarted.

TheTask<V> class implements theWorker<V> interface. Therefore, all properties and methods specified by theWorker<V> interface are available in theTask<V> class.

TheTask<V> class inherits from theFutureTask<V> class, which is part of the Java Concurrency Framework.

TheFutureTask<V> implements theFuture<V>,RunnableFuture<V>, andRunnable interfaces.

Therefore, aTask<V> also implements all these interfaces.

3.1 The Code

PrimeFinderTask.java

import javafx.collections.FXCollections;import javafx.collections.ObservableList;import javafx.concurrent.Task;public class PrimeFinderTask extends Task<ObservableList<Long>>{// Define the Limitsprivate long lowerLimit = 1;private long upperLimit = 30;private long sleepTimeInMillis = 500;public PrimeFinderTask() {}public PrimeFinderTask(long lowerLimit, long upperLimit) {this.lowerLimit = lowerLimit;this.upperLimit = upperLimit;}public PrimeFinderTask(long lowerLimit,long upperLimit,long sleepTimeInMillis) {this(lowerLimit, upperLimit);this.sleepTimeInMillis = sleepTimeInMillis;}// The task implementation@Overrideprotected ObservableList<Long> call() {// An observable list to represent the resultsfinal ObservableList<Long> results = FXCollections.<Long>observableArrayList();// Update the titlethis.updateTitle("Prime Number Finder Task");long count = this.upperLimit - this.lowerLimit + 1;long counter = 0;// Find the prime numbersfor (long i = lowerLimit; i <= upperLimit; i++) {// Check if the task is cancelledif (this.isCancelled()) {break;}// Increment the countercounter++;// Update messagethis.updateMessage("Checking " + i + " for a prime number");// Sleep for some timetry {Thread.sleep(this.sleepTimeInMillis);}catch (InterruptedException e) {// Check if the task is cancelledif (this.isCancelled()) {break;}}// Check if the number is a prime numberif (PrimeUtil.isPrime(i)) {// Add to the listresults.add(i);// Publish the read-only list to give the GUI // access to the partial resultsupdateValue(FXCollections.<Long>unmodifiableObservableList(results));}// Update the progressupdateProgress(counter, count);}return results;}@Overrideprotected void cancelled() {super.cancelled();updateMessage("The task was cancelled.");}@Overrideprotected void failed() {super.failed();updateMessage("The task failed.");}@Overridepublic void succeeded() {super.succeeded();updateMessage("The task finished successfully.");}}

The above program is an implementation of theTask<ObservableList<Long>>. It checks for prime numbers between the specifiedlowerLimit andupperLimit. It returns all the numbers in the range. Notice that the task thread sleeps for a short time before checking a number for a prime number. This is done to give the user an impression of a long-running task.

It is not needed in a real world application. Thecall() method handles anInterruptedException and finishes the task if the task was interrupted as part of a cancellation request. The call to the methodupdateValue() needs little explanation.

updateValue(FXCollections.<Long>unmodifiableObservableList(results));

Every time a prime number is found, the results list is updated. The foregoing statement wraps the results list in an unmodifiable observable list and publishes it for the client. This gives the client access to the partial results of the task. This is a quick and dirty way of publishing the partial results. If thecall() method returns a primitive value, it is fine to call theupdateValue() method repeatedly.

The following program contains the complete code to build a GUI using yourPrimeFinderTask class.

FxConcurrentExample1.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.layout.BorderPane;import javafx.scene.layout.GridPane;import javafx.scene.layout.HBox;import javafx.stage.Stage;import static javafx.concurrent.Worker.State.READY;import static javafx.concurrent.Worker.State.RUNNING;public class FxConcurrentExample1 extends Application{// Create the ButtonsButton startButton = new Button("Start");Button cancelButton = new Button("Cancel");Button exitButton = new Button("Exit");// Create the taskPrimeFinderTask task = new PrimeFinderTask();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();            }        });cancelButton.setOnAction(new EventHandler <ActionEvent>() {            public void handle(ActionEvent event)             {            task.cancel();            }        });// Enable/Disable the Start and Cancel buttonsstartButton.disableProperty().bind(task.stateProperty().isNotEqualTo(READY));cancelButton.disableProperty().bind(task.stateProperty().isNotEqualTo(RUNNING));// Create the GridPaneGridPane pane = new WorkerStateGUI(task);// Create the ButtonBoxHBox buttonBox = new HBox(5, startButton, cancelButton, exitButton);// Create the BorderPaneBorderPane root = new BorderPane();root.setCenter(pane);root.setBottom(buttonBox);// Set the Style-properties of the BorderPaneroot.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,500,400);// Add the scene to the Stagestage.setScene(scene);// Set the title of the Stagestage.setTitle("A Prime Number Finder Task");// Display the Stagestage.show();}public void startTask() {// Schedule the task on a background threadThread backgroundThread = new Thread(task);backgroundThread.setDaemon(true);backgroundThread.start();}}

3.2 Creating a Task

Creating aTask<V> is easy. You need to subclass theTask<V> class and provide an implementation for the abstract methodcall(). Thecall() method contains the logic to perform theTask.

The following snippet of code shows the skeleton of aTask implementation:

// The task implementation@Overrideprotected ObservableList<Long> call() {// An observable list to represent the resultsfinal ObservableList<Long> results = FXCollections.<Long>observableArrayList();// Update the titlethis.updateTitle("Prime Number Finder Task");long count = this.upperLimit - this.lowerLimit + 1;long counter = 0;// Find the prime numbersfor (long i = lowerLimit; i <= upperLimit; i++) {// Check if the task is cancelledif (this.isCancelled()) {break;}// Increment the countercounter++;// Update messagethis.updateMessage("Checking " + i + " for a prime number");// Sleep for some timetry {Thread.sleep(this.sleepTimeInMillis);}catch (InterruptedException e) {// Check if the task is cancelledif (this.isCancelled()) {break;}}// Check if the number is a prime numberif (PrimeUtil.isPrime(i)) {// Add to the listresults.add(i);// Publish the read-only list to give the GUI // access to the partial resultsupdateValue(FXCollections.<Long>unmodifiableObservableList(results));}// Update the progressupdateProgress(counter, count);}return results;}

3.3 Updating Task Properties

Typically, you would want to update the properties of theTask as it progresses. The properties must be updated and read on the JavaFXApplicationThread, so they can be observed safely in a GUI environment. TheTask<V> class provides special methods to update some of its properties.

  • protected void updateMessage(String message)
  • protected void updateProgress(double workDone, double totalWork)
  • protected void updateProgress(long workDone, long totalWork)
  • protected void updateTitle(String title)
  • protected void updateValue(V value)

You provide the values for theworkDone and thetotalWork properties to theupdateProgress() method. The progress property will be set toworkDone/totalWork. The method throws a runtime exception if the workDone is greater than thetotalWork or both are less than -1.0.

Sometimes, you may want to publish partial results of a task in its value property. TheupdateValue() method is used for this purpose. The final result of a task is the return value of itscall() method.

AllupdateXxx() methods are executed on the JavaFXApplicationThread. Their names indicate the property they update. They are safe to be called from thecall() method of theTask.

If you want to update the properties of theTask from thecall() method directly, you need to wrap the code inside aPlatform.runLater() call.

3.4 Listening to Task Transition Events

TheTask class contains the following properties to let you set event handlers for its state transitions:

  • onCancelled
  • onFailed
  • onRunning
  • onScheduled
  • onSucceeded

3.5 Cancelling a Task

Use one of the following twocancel() methods to cancel a task:

  • public final boolean cancel()
  • public boolean cancel(boolean mayInterruptIfRunning)

The first version removes theTask from the execution queue or stops its execution.

The second version lets you specify whether the thread running theTask be interrupted.

Make sure to handle theInterruptedException inside thecall() method. Once you detect this exception, you need to finish thecall() method quickly. Otherwise, the call tocancel(true) may not cancel the task reliably. Thecancel() method may be called from any thread.

The following methods of theTask are called when it reaches a specific state:

  • protected void scheduled()
  • protected void running()
  • protected void succeeded()
  • protected void cancelled()
  • protected void failed()

Their implementations in theTask class are empty. They are meant to be overridden by the subclasses.

3.6 Running a Task

ATask isRunnable as well as aFutureTask. To run it, you can use a background thread or anExecutorService.

// Schedule the task on a background threadThread backgroundThread = new Thread(task);backgroundThread.setDaemon(true);backgroundThread.start();

3.7 The GUI

The following image shows the window after starting the program

The output of the Program before starting the Task
The output of the Program before starting the Task

The following Figure shows the window when the task is running. You will need to click the Start button to start the task.

The output of the Program during the execution of the Task
The output of the Program during the execution of the Task

Clicking the Cancel button cancels the task. Once the task finishes, it is cancelled or it fails; you cannot restart it and both the Start and Cancel buttons are disabled.

The output of the Program after canceling the Task
The output of the Program after canceling the Task

Notice that when the task finds a new prime number, it is displayed on the window immediately.

After execution of theTask, the result will be shown:

The output of the Program after finishing the Task
The output of the Program after finishing the Task

4. Using the Service Class

TheService<V> class is an implementation of theWorker<V> interface. It encapsulates aTask<V>. It makes theTask<V> reusable by letting it be started, cancelled, reset, and restarted.

4.1 The Code

The following program shows how to use aService. TheService object is created and stored as an instance variable. TheService object manages aPrimeFinderTask object, which is aTask to find prime numbers between two numbers.

Four buttons are added: Start/Restart, Cancel, Reset, and Exit. The StartButton is labeled Restart after theService is started for the first time. The buttons do what their labels indicate. Buttons are disabled when they are not applicative.

FxConcurrentExample2.java

import javafx.application.Application;import javafx.application.Platform;import javafx.beans.binding.Bindings;import javafx.collections.ObservableList;import javafx.concurrent.Service;import javafx.concurrent.Task;import javafx.event.ActionEvent;import javafx.event.EventHandler;import static javafx.concurrent.Worker.State.RUNNING;import static javafx.concurrent.Worker.State.SCHEDULED;import javafx.scene.Scene;import javafx.scene.control.Button;import javafx.scene.layout.BorderPane;import javafx.scene.layout.GridPane;import javafx.scene.layout.HBox;import javafx.stage.Stage;public class FxConcurrentExample2 extends Application{// Create the ButtonsButton startButton = new Button("Start");Button cancelButton = new Button("Cancel");Button exitButton = new Button("Exit");Button resetButton = new Button("Reset");boolean onceStarted = false;public static void main(String[] args) {Application.launch(args);}@Overridepublic void start(Stage stage) {// Create the Event-Handlers for the ButtonsstartButton.setOnAction(new EventHandler <ActionEvent>() {            public void handle(ActionEvent event)             {            if (onceStarted)             {            service.restart();            }             else             {            service.start();            onceStarted = true;            startButton.setText("Restart");            }            }        });exitButton.setOnAction(new EventHandler <ActionEvent>() {            public void handle(ActionEvent event)             {            Platform.exit();            }        });cancelButton.setOnAction(new EventHandler <ActionEvent>() {            public void handle(ActionEvent event)             {            service.cancel();            }        });resetButton.setOnAction(new EventHandler <ActionEvent>() {            public void handle(ActionEvent event)             {            service.reset();            }        });// Enable/Disable the Reset and Cancel buttonscancelButton.disableProperty().bind(service.stateProperty().isNotEqualTo(RUNNING));resetButton.disableProperty().bind(Bindings.or(service.stateProperty().isEqualTo(RUNNING),service.stateProperty().isEqualTo(SCHEDULED)));// Create the GridPaneGridPane pane = new WorkerStateGUI(service);// Create the ButtonBoxHBox buttonBox = new HBox(5, startButton, cancelButton, resetButton, exitButton);// Create the BorderPaneBorderPane root = new BorderPane();root.setCenter(pane);root.setBottom(buttonBox);// Set the Style-properties of the BorderPaneroot.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,500,400);// Add the scene to the Stagestage.setScene(scene);// Set the title of the Stagestage.setTitle("A Prime Number Finder Task");// Display the Stagestage.show();}// Create the serviceService<ObservableList<Long>> service = new Service<ObservableList<Long>>() {@Overrideprotected Task<ObservableList<Long>> createTask() {return new PrimeFinderTask();}};}

4.2 Creating a Service

Remember that aService<V> encapsulates aTask<V>. Therefore, you need aTask<V> to have aService<V>.

Want to master JavaFX ?
Subscribe to our newsletter and download theJavaFXProgramming Cookbookright now!
In order to get you prepared for your JavaFX development needs, we have compiled numerous recipes to help you kick-start your projects. Besides reading them online you may download the eBook in PDF format!

Thank you!

We will contact you soon.

TheService<V> class contains an abstract protectedcreateTask() method that returns aTask<V>.

To create a service, you need to subclass theService<V> class and provide an implementation for thecreateTask() method.

The following snippet of code creates a Service that encapsulates aPrimeFinderTask, which you have created earlier:

// Create the serviceService<ObservableList<Long>> service = new Service<ObservableList<Long>>() {@Overrideprotected Task<ObservableList<Long>> createTask() {return new PrimeFinderTask();}};

ThecreateTask() method of the service is called whenever the service is started or restarted.

4.3 Updating Service Properties

TheService class contains all properties that represent the internal
state of aWorker. It adds an executor property, which is ajava.util.concurrent.Executor.

The property is used to run theService. If it is not specified, a daemon thread is created to run theService.

Unlike theTask class, theService class does not containupdateXxx() methods for updating its properties. Its properties are bound to the corresponding properties of the underlyingTask<V>.

When theTask updates its properties, the changes are reflected automatically to theService and to the client.

4.4 Cancelling the Service

Use thecancel() methods to cancel aService. The method sets the state of theService toCANCELLED.

The following snippet of code shows an example:

service.cancel();

4.5 Starting the Service

Calling thestart() method of theService class starts aService. The method calls thecreateTask() method to get aTask instance and runs theTask. TheService must be in theREADY state when itsstart() method is called.

The following snippet of code shows an example:

service.start();

4.6 Resetting the Service

Calling thereset() method of theService class resets theService. Resetting puts all theService properties back to their initial states. The state is set toREADY.

Resetting aService is allowed only when theService
is in one of the finish states:SUCCEEDED,FAILED,CANCELLED, orREADY. Calling thereset() method throws a runtime exception if theService is in theSCHEDULED orRUNNING state.

The following snippet of code shows an example:

service.reset();

4.7 Restarting the Service

Calling therestart() method of theService class restarts aService. It cancels the task if it exists, resets the service, and starts it. It calls the three methods on theService object in sequence.

  • cancel()
  • reset()
  • start()

The following snippet of code shows an example:

service.restart();

4.8 The GUI

The following window shows the program after starting:

The output of the Program before starting the Task
The output of the Program before starting the Task

The following GUI shows the program after pressing the StartButton:

The output of the Program during the execution of the Task
The output of the Program during the execution of the Task

After pressing the CancelButton, the following window will appear:

The output of the Program after cancelling the running Task
The output of the Program after cancelling the running Task

The following GUI shows the program after pressing the RestartButton:

The output of the Program during the execution of the Task
The output of the Program during the execution of the Task

5. Using the ScheduledService Class

TheScheduledService<V> is aService<V>, which automatically restarts. It can restart when it finishes successfully or when it fails. Restarting on a failure is configurable. TheScheduledService<V> class inherits from theService<V> class. TheScheduledService is suitable for tasks that use polling.

5.1 The Code

FxConcurrentExample3.java

import javafx.application.Application;import javafx.application.Platform;import javafx.beans.binding.Bindings;import javafx.collections.ObservableList;import javafx.concurrent.ScheduledService;import javafx.concurrent.Task;import javafx.event.ActionEvent;import javafx.event.EventHandler;import static javafx.concurrent.Worker.State.RUNNING;import static javafx.concurrent.Worker.State.SCHEDULED;import javafx.scene.Scene;import javafx.scene.control.Button;import javafx.scene.layout.BorderPane;import javafx.scene.layout.GridPane;import javafx.scene.layout.HBox;import javafx.stage.Stage;import javafx.util.Duration;public class FxConcurrentExample3  extends Application{// Create the ButtonsButton startButton = new Button("Start");Button cancelButton = new Button("Cancel");Button exitButton = new Button("Exit");Button resetButton = new Button("Reset");boolean onceStarted = false;// Create the scheduled serviceScheduledService<ObservableList<Long>> service =new ScheduledService<ObservableList<Long>>() {@Overrideprotected Task<ObservableList<Long>> createTask() {return new PrimeFinderTask();}};public static void main(String[] args) {Application.launch(args);}@Overridepublic void start(Stage stage) {// Configure the scheduled serviceservice.setDelay(Duration.seconds(5));service.setPeriod(Duration.seconds(30));service.setMaximumFailureCount(5);// Create the Event-Handlers for the ButtonsstartButton.setOnAction(new EventHandler <ActionEvent>() {            public void handle(ActionEvent event)             {            if (onceStarted)             {            service.restart();            }             else             {            service.start();            onceStarted = true;            startButton.setText("Restart");            }            }        });exitButton.setOnAction(new EventHandler <ActionEvent>() {            public void handle(ActionEvent event)             {            Platform.exit();            }        });cancelButton.setOnAction(new EventHandler <ActionEvent>() {            public void handle(ActionEvent event)             {            service.cancel();            }        });resetButton.setOnAction(new EventHandler <ActionEvent>() {            public void handle(ActionEvent event)             {            service.reset();            }        });// Enable/Disable the Reset and Cancel buttonscancelButton.disableProperty().bind(service.stateProperty().isNotEqualTo(RUNNING));resetButton.disableProperty().bind(Bindings.or(service.stateProperty().isEqualTo(RUNNING),service.stateProperty().isEqualTo(SCHEDULED)));// Create the GridPaneGridPane pane = new WorkerStateGUI(service);// Create the ButtonBoxHBox buttonBox = new HBox(5, startButton, cancelButton, resetButton, exitButton);// Create the BorderPaneBorderPane root = new BorderPane();root.setCenter(pane);root.setBottom(buttonBox);// Set the Style-properties of the BorderPaneroot.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,500,400);// Add the scene to the Stagestage.setScene(scene);// Set the title of the Stagestage.setTitle("A Prime Number Finder Task");// Display the Stagestage.show();}}

5.2 Creating a ScheduledService

The process of creating aScheduledService is the same as that of creating aService. You need to subclass theScheduledService<V> class and provide an implementation for thecreateTask() method.

The following snippet of code creates aScheduledService that encapsulates aPrimeFinderTask, which you have created earlier:

// Create the scheduled serviceScheduledService<ObservableList<Long>> service =new ScheduledService<ObservableList<Long>>() {@Overrideprotected Task<ObservableList<Long>> createTask() {return new PrimeFinderTask();}};

ThecreateTask() method of the service is called when the service is started or restarted manually or automatically.

Note that aScheduledService is automatically restarted. You can start and restart it manually by calling thestart() andrestart() methods.

5.3 Updating ScheduledService Properties

TheScheduledService<V> class inherits properties from theService<V> class. It adds the following properties that can be used to configure the scheduling of theService.

  • lastValue
  • delay
  • period
  • restartOnFailure
  • maximumFailureCount
  • backoffStrategy
  • cumulativePeriod
  • currentFailureCount
  • maximumCumulativePeriod

AScheduledService<V> is designed to run several times. The current value computed by theService is not very meaningful. Your class adds a new propertylastValue, which is of the typeV, and it is the last value computed by theService.

Thedelay is a duration, which specifies a delay between when theService is started and when it begins running. TheService stays in theSCHEDULED state for the specified delay. The delay is honored only when theService is started manually calling thestart() orrestart() method. When theService is restarted automatically, honoring the delay property depends on the current state of theService.

Theperiod is a duration, which specifies the minimum amount of time between the last run and the next run. The default period is zero.

TherestartOnFailure specifies whether theService restarts automatically when it fails. By default, it is set to true.

ThecurrentFailureCount is the number of times the scheduledService has failed. It is reset to zero when the scheduledService is restarted manually.

ThemaximumFailureCount specifies the maximum number of times theService can fail before it is transitioned into theFAILED state and it is not automatically restarted again.

ThebackoffStrategy is aCallback<ScheduledService<?>,Duration> that computes the duration to add to the period on each failure. Typically, if aService fails, you want to slow down before retrying it.

Suppose aService runs every 10 minutes.

Thererun gaps are computed based on the non-zero period and the current failure count.

ThecumulativePeriod is a duration, which is the time between the current failed run and the next run.

5.4 Listening to ScheduledService Transition Events

TheScheduledService goes through the same transition states as theService. It goes through theREADY,SCHEDULED, andRUNNING states automatically after a successful run. Depending on how the scheduled service is configured, it may go through the same state transitions automatically after a failed run.

You can listen to the state transitions and override the transition-related methods as you can for aService. When you override the transition-related methods in aScheduledService subclass, make sure to call the super method to keep yourScheduledService working properly.

5.5 The GUI

The following image show the state of theScheduledService when it is not started:

The output of the Program before starting the Task
The output of the Program before starting the Task

The next image shows theService, when it is running:

The output of the Program during the execution of the Task
The output of the Program during the execution of the Task

The following imgae shows the program after cancelling:

The output of the Program after resetting the running Task
The output of the Program after resetting the running Task

The last image shows the application after restarting:

The output of the Program after canceling the running Task
The output of the Program after canceling the running Task

6. Download Java Source Code

This was an example ofjavafx.concurrent

Download
You can download the full source code of this example here:JavaFxConcurrentExample.zip
Do you want to know how to develop your skillset to become aJava Rockstar?
Subscribe to our newsletter to start Rockingright now!
To get you started we give you our best selling eBooks forFREE!
1. JPA Mini Book
2. JVM Troubleshooting Guide
3. JUnit Tutorial for Unit Testing
4. Java Annotations Tutorial
5. Java Interview Questions
6. Spring Interview Questions
7. Android UI Design
and many more ....
I agree to theTerms andPrivacy Policy

Thank you!

We will contact you soon.

Photo of Andreas PomarolliAndreas PomarolliJuly 28th, 2016Last Updated: April 24th, 2019
2 1,083 18 minutes read
Photo of Andreas Pomarolli

Andreas Pomarolli

Andreas has graduated from Computer Science and Bioinformatics at the University of Linz. During his studies he has been involved with a large number of research projects ranging from software engineering to data engineering and at least web engineering. His scientific focus includes the areas of software engineering, data engineering, web engineering and project management. He currently works as a software engineer in the IT sector where he is mainly involved with projects based on Java, Databases and Web Technologies.

Related Articles

The JavaFX Print API

July 8th, 2016

JavaFX WebView Example

October 13th, 2016

JavaFX Canvas Example

May 18th, 2016

JavaFX CSS Tutorial

May 5th, 2016

The JavaFX Media API

August 8th, 2016
Subscribe
Notify of
guest
I agree to theTerms andPrivacy Policy
The comment form collects your name, email and content to allow us keep track of the comments placed on the website. Please read and accept our website Terms and Privacy Policy to post a comment.

I agree to theTerms andPrivacy Policy
The comment form collects your name, email and content to allow us keep track of the comments placed on the website. Please read and accept our website Terms and Privacy Policy to post a comment.