过去几天我一直在尝试 JavaFX、FXML、任务和属性。我偶然发现了一种奇怪的行为,希望您能帮助我更好地了解发生了什么。
我有一个简约的 GUI,如下所示: GUI
如果我单击按钮,则会创建并启动一个新任务。此任务递增 Double 属性,并将新值写入标签上并设置在进度栏中。任务的代码可以在这里看到:
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 Controller 的代码如下:
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 应用程序线程上;当前线程 = 线程 4。 slider 的更新工作正常并且不会抛出任何错误。
阅读以下有关 SO 的问题后:
我知道标签的更新失败,因为我试图在监听器中执行 UI 更新,该更新是从 TestTask 线程而不是从 FXApplication 线程调用的。
我现在的问题是:为什么 slider 的更新可以工作并且不会引发异常?更新是在监听器内执行的,因此是在 TestTask 线程内执行的。此尝试是否还应该引发“不在 FX 应用程序线程上”异常?
预先感谢您抽出时间来帮助我。
最佳答案
在监听另一个线程上的属性更改时更新一个线程上的属性只是 race condition 。不要这样做。
JavaFX 系统不仅假设 UI 的更新发生在单个线程上,而且还假设属性的更新发生在单个线程上,无论它们是否绑定(bind)到 UI 元素。不要依赖 JavaFX 系统来检查所有竞争条件并为您正确报告和处理它们,因为它的设计目的不是这样做。
您需要以防止出现此类情况的方式编写代码。
关于JavaFX:仅在更新标签时不在应用程序线程中?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46993547/