I have a simple JavaFX 2 app, with 2 buttons, saying Start and Stop. When the start button is clicked, I want to create a background thread which will do some processing and update the UI (e.g a progress bar) as it goes along. If the stop button is clicked, I want the thread to terminate.
I've tried to do this using the javafx.concurrent.Task
class which I gathered from the documentation would work fine for this. But whenever I click Start, the UI freezes/hangs rather than staying normal.
Her's the code from the main Myprogram extends Application
class for showing the buttons:
public void start(Stage primaryStage) { final Button btn = new Button(); btn.setText("Begin"); //This is the thread, extending javafx.concurrent.Task : final MyProcessor handler = new MyProcessor(); btn.setOnAction(new EventHandler<ActionEvent>() { public void handle(ActionEvent event) { handler.run(); } }); Button stop = new Button(); stop.setText("Stop"); stop.setOnAction(new EventHandler<ActionEvent>() { public void handle(ActionEvent event) { handler.cancel(); } } ); // Code for adding the UI controls to the stage here. }
Here's the code of MyProcessor
class:
import javafx.concurrent.Task; public class MyProcessor extends Task { @Override protected Integer call() { int i = 0; for (String symbol : feed.getSymbols() ) { if ( isCancelled() ) { Logger.log("Stopping!"); return i; } i++; Logger.log("Doing # " + i); //Processing code here which takes 2-3 seconds per iteration to execute Logger.log("# " + i + ", DONE! "); } return i; } }
Pretty simple, but the UI hangs whenever I click the Start button, though the console messages continue to get displayed (Logger.log
simply does System.out.println
)
What am I doing wrong?
Thread safety in a JavaFX application cannot be achieved by synchronizing thread actions. We must ensure that the programs that manipulate the scene graph must do so only from the JavaFX Application Thread. Therefore, multithreading in JavaFX has to be handled in a different manner.
application. Application#stop() is called before the context terminates. Put inside the stop() method everything that needs to be executed before the JavaFX context terminates. With the System.
JavaFX uses a single-threaded rendering design, meaning only a single thread can render anything on the screen, and that is the JavaFX application thread. In fact, only the JavaFX application thread is allowed to make any changes to the JavaFX Scene Graph in general.
Task
implements Runnable
, so when you call handler.run();
you actually run the call
method in the UI Thread. That will hang the UI.
You should start the task in a background thread, either via an executor or simply by calling new Thread(handler).start();
.
This is explained (maybe not very clearly) in the javadoc or in the JavaFX concurrency tutorial.
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