2017-10-28 189 views
2

最近几天我一直在使用JavaFX,FXML,任务和属性进行实验。我偶然发现了一种奇怪的行为,希望你能帮助我更好地理解发生了什么。JavaFX:仅在更新标签时才应用线程JavaFX?

我有一个简约的GUI,看起来像这样:GUI

如果我按一下按钮创建并启动了新的任务。此任务将增加一个双重属性,并将新值写入标签并在ProgressBar中进行设置。任务的代码可以在这里看到:

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

的FXML控制器的代码如下:

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

如果我运行这段代码试图更新以下异常标签结果:java.lang.IllegalStateException:不在FX应用程序线程上; currentThread =线程4。滑块的更新工作正常,并没有抛出任何东西。

阅读SO下列问题后:

我明白,因为我试图内执行UI更新标签的更新失败Listener从TestTask线程调用,而不是从FXApplication线程调用。

我的问题是:为什么更新的滑块工作和不会引发异常?更新在Listener内进行,因此在TestTask Thread内进行。不应该这个尝试还会抛出“不在FX应用程序线程”异常?

在此先感谢您花时间帮助我。

+3

有时会检查线程,有时不会。它不一致。尽管如此,场景中的节点应该从应用程序线程更新... – fabian

+0

@fabian但看到这样的行为仍然很奇怪。即使我减少TestTask内的睡眠时间并增加迭代次数,每次都能顺利进行。当然,每个UI更新都必须在JavaFX线程内,但这种情况仍然很有趣。我会看看滑块课,也许我会在那里找到答案。 – JKostikiadis

回答

3

在您侦听另一个线程上的属性更改时更新一个线程上的属性只是一个race condition。不要这样做。

不仅JavaFX系统假定更新的UI发生在单个线程上,它也假定对属性相同,无论它们是否绑定到UI元素。不要依赖JavaFX系统来检查所有竞争条件,并为您正确报告和处理它们,因为它不是为了这样做而设计的。

您需要编写您的代码,以防止出现这种情况。