Throttling javafx gui updates

Ricevo oggetti dati in momentjs casuali ad alta frequenza e devo aggiornare la GUI JavaFX con questi. Tuttavia, non voglio riempire la coda degli eventi javafx con un numero molto elevato di runnables (io uso Platform.RunLater).

Ho pensato a come implementare al meglio un algoritmo di limitazione.

  • Sarebbe meglio avere un thread GUIUpdater separato che controlli ad esempio una coda di blocco per i nuovi oggetti, quindi dorme ad esempio per 30 ms e poi controlla di nuovo, in un ciclo infinito? In tal caso, una coda di blocco sarebbe la struttura dati ottimale? Si noti che ho solo bisogno dell’ultimo object dati e che blockingQueue è una coda FIFO e non riesco a selezionare solo la voce più recente.
  • Oppure – sarebbe meglio semplicemente aggiornare la GUI con Platform.RunLater se nanoTime-startTime> 30ms? In tal caso, non ho bisogno di un thread separato per eseguire Platform.RunLater-call. Tuttavia, se viene ricevuto un aggiornamento quando non è passato 30 ms e quindi non vengono ricevuti aggiornamenti per un po ‘di tempo, l’ultimo aggiornamento non verrà visualizzato nella GUI.

Qualche suggerimento su come progettare un algoritmo di limitazione per JavaFX Platform.RunLater GUI si aggiorna in modo breve ed efficiente?

Questo è l’idioma utilizzato nella class Task per l’implementazione del updateMessage(...) e altri metodi simili. Fornisce una soluzione piacevole e robusta per evitare di inondare il thread dell’applicazione FX:

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

AtomicLong contiene il valore corrente da utilizzare per aggiornare l’etichetta. Il conteggio incrementa e aggiorna continuamente AtomicLong , ma pianifica solo una chiamata a Platform.runLater(...) se il suo valore corrente è -1. Platform.runLater(...) aggiorna l’ Label con il valore corrente da AtomicLong e riporta AtomicLong indietro a -1, a indicare che è pronto per un nuovo aggiornamento.

L’effetto qui è di pianificare nuove chiamate a Platform.runLater(...) ogni volta che il thread dell’applicazione FX è pronto a gestirli. Non c’è un intervallo di tempo codificato che potrebbe richiedere la messa a punto.