2

for the last few days I have been experimenting with JavaFX, FXML, Tasks and Properties. I stumbled upon a strange behaviour and hope that you can help me to understand better what's going on.

I have a minimalistic GUI which looks like this: GUI

If I click on the Button a new Task is created and started. This Task increments a Double Property and the new Value is written onto the Label and set in the ProgressBar. The Code of the Task can be seen here:

public class TestTask extends Task<Void>{

    private final DoubleProperty doubleValue = new SimpleDoubleProperty();

    @Override
    protected Void call() throws Exception {
        for(double i = 0.1; i <= 1; i = i+0.1 ) {
            doubleValue.set(i);
            Thread.sleep(1000);
        }
        return null;
    }

    public DoubleProperty test() {
        return doubleValue;
    }
}

The code of the FXML Controller is the following:

public class FXMLDocumentController implements Initializable {

    @FXML private Label label;
    @FXML private Slider slider;

    //Called when the Button is clicked
    @FXML
    private void handleButtonAction(ActionEvent event) {
        TestTask task =  new TestTask();
        task.test().addListener((ObservableValue<? extends Number> observable, Number oldValue, Number newValue) -> {
            //Works without problems
            slider.setValue(newValue.doubleValue());
            //Throws an exception
            label.setText("Value" + newValue.doubleValue());
        });
        new Thread(task).start();
    }
}

If I run this code the attempt to update the Label results in the following exception: java.lang.IllegalStateException: Not on FX application thread; currentThread = Thread-4. The update of the Slider works fine and throws no exptions.

After reading the following questions on SO:

I understand that the update of the Label fails because I'm trying to execute an UI Update within the Listener which is called from the TestTask Thread and not from the FXApplication Thread.

My question is now: Why does the Update of the Slider work und doesn't throw an exception? The update is carried out within the Listener and therefore from within the TestTask Thread. Shouldn't this attempt also throw a "Not on FX application thread" exception?

Thanks in advance for taking your time to help me.

  • 3
    Sometimes the thread is checked, sometimes not. Its not consistent. Nonetheless nodes in a scene should be updated form the application thread... – fabian Oct 28 '17 at 19:40
  • @fabian But still it's very strange to see such a behavior. Even when I reduce the sleep time inside the TestTask and increase the for iterations everything works smoothly every time. Of course every UI update MUST be inside the JavaFX thread but still this case is interesting. I will have a look at slider class maybe I will find an answer there. – JKostikiadis Oct 28 '17 at 19:54

1 Answers1

3

Updating a property on one thread while you listen for changes to the property on another thread is just a race condition. Don't do it.

Not only does the JavaFX system assume updates to the UI occur on a single thread, it also assumes the same for properties, regardless of whether they are bound to UI elements. Don't rely on the JavaFX system to check for all race conditions and report and handle them correctly for you, because it is not designed to do that.

You need write your code in such a way as to prevent such conditions.

jewelsea
  • 130,119
  • 12
  • 333
  • 365