Throttling javafx gui updates Throttling javafx gui updates multithreading multithreading

Throttling javafx gui updates


This is the idiom used in the Task class for implementing the updateMessage(...) method, and other similar methods. It provides a nice, robust solution to avoid flooding the FX Application Thread:

import java.util.concurrent.atomic.AtomicLong;import javafx.application.Application;import javafx.application.Platform;import javafx.geometry.Insets;import javafx.geometry.Pos;import javafx.scene.Scene;import javafx.scene.control.Label;import javafx.scene.layout.VBox;import javafx.stage.Stage;public class ThrottlingCounter extends Application {    @Override    public void start(Stage primaryStage) {        final AtomicLong counter = new AtomicLong(-1);        final Label label = new Label();        final Thread countThread = new Thread(new Runnable() {            @Override            public void run() {                long count = 0 ;                while (true) {                    count++ ;                    if (counter.getAndSet(count) == -1) {                        updateUI(counter, label);                    }                }            }        });        countThread.setDaemon(true);        countThread.start();        VBox root = new VBox();        root.getChildren().add(label);        root.setPadding(new Insets(5));        root.setAlignment(Pos.CENTER);        Scene scene = new Scene(root, 150, 100);        primaryStage.setScene(scene);        primaryStage.show();    }    private void updateUI(final AtomicLong counter,            final Label label) {        Platform.runLater(new Runnable() {            @Override            public void run() {                final String msg = String.format("Count: %,d", counter.getAndSet(-1));                label.setText(msg);            }        });    }    public static void main(String[] args) {        launch(args);    }}

The AtomicLong holds the current value to be used to update the Label. The count continually increments and updates the AtomicLong, but only schedules a call to Platform.runLater(...) if it's current value is -1. The Platform.runLater(...) updates the Label with the current value from the AtomicLong and flips the AtomicLong back to -1, indicating that it's ready for a new update.

The effect here is to schedule new calls to Platform.runLater(...) whenever the FX Application Thread is ready to handle them. There's no hard-coded time interval which could need tuning.