当前位置: 首页 > 知识库问答 >
问题:

JavaFX:仅在更新标签时不在应用程序线程中?

吴升
2023-03-14

在过去的几天里,我一直在尝试JavaFX、FXML、任务和属性。我偶然发现了一种奇怪的行为,希望您能帮助我更好地理解正在发生的事情。

我有一个极简的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应用程序线程上;电流线程=线程-4。滑块的更新工作正常,不会引发任何扩展。

阅读以下关于SO的问题后:

  • 在哪个线程上执行JavaFX更改侦听器?
  • 使用线程发出数据库请求

我知道标签的更新失败了,因为我试图在监听器中执行一个UI更新,该更新是从TestTask线程调用的,而不是从FXApplication线程调用的。

我现在的问题是:为什么滑块的更新工作并且没有抛出异常?更新是在监听器中进行的,因此是在TestTask线程中进行的。这种尝试不应该也抛出“不在FX应用程序线程上”异常吗?

提前感谢您抽出时间来帮助我。

共有1个答案

安毅
2023-03-14

在监听另一个线程上的属性更改时更新一个线程上的属性只是竞争条件。不要这样做。

JavaFX系统不仅假设UI的更新发生在单个线程上,还假设属性的更新发生在单个线程上,无论它们是否绑定到UI元素。不要依赖JavaFX系统来检查所有比赛情况,并为您报告和正确处理这些情况,因为它不是专门为这样做而设计的。

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

 类似资料:
  • 问题内容: 我正在用这个 但是这个: 给我这个错误: 而且我真的不明白怎么了 问题答案: 可能是因为您误解了工作原理。 正确的代码段为: 但: 我强烈建议您不要使用,而是使用!它是JavaFX API的一部分,您不必执行这些调用。这只是很快就被黑了,但是您知道了:

  • 问题内容: 我正在尝试使用各种状态消息为应用程序异步更新JavaFx GUI中的标签。 例如 我的应用程序中的“更新”按钮在控制器中调用方法updateSettings()。现在,我尝试以以下方式更新UI上的标签。 我希望这些消息应在流程执行时显示在标签中,以向用户显示应用程序中正在进行的各种活动。 如何实现这种行为? 问题答案: 您可以在JavaFX应用程序线程之外(在Task中)运行耗时的方法

  • 问题内容: 我同时使用Javafx和线程,并且遇到了这个问题,我制作了一个按钮,然后单击该按钮(使用事件处理程序)时,我做了一个for循环,将按钮更改为1,2,3,4 ,5,然后在每个中间延迟一秒钟。像倒计时! 但是发生的事情是延迟了5秒钟,并将按钮的文本更改为5。 问题是我希望看到它在1到5之间变化,但我看到的只是在5秒延迟结束时为5。我认为它会更改按钮文本,但看不到。我可能与类中的方法有关。

  • 我同时使用Javafx和线程,我经常遇到这个问题,我制作了一个按钮,然后当单击按钮时(使用事件处理程序),我制作了一个for循环,将按钮更改为1、2、3、4、5,然后在每个按钮中间延迟一秒钟。就像倒计时一样! 但实际情况是,它会延迟5秒钟,并将按钮的文本更改为5。 问题是我想看到它在1到5之间变化,但我看到的只是5秒延迟结束时的5。我假设它会更改按钮文本,但我看不到它。我可能不得不处理<代码>。在

  • 当我创建一个简单的非多线程JavaFX应用程序并启动它时,该应用程序会创建一些线程(JavaFXApplicationThread、JavaFXLauncher等)。这些线程中的大多数都已命名,但在我的所有JavaFX应用程序中都有一个未命名的线程(“线程-1”或“线程-2”)。我绝对不会创建自己的线程,因为我尝试启动Hello World JavaFX应用程序(由IDEA生成),其中也包含“线程

  • JavaFX场景图表示JavaFX应用程序的图形用户界面,它不是线程安全的,只能从UI线程(也称为JavaFX应用程序线程)访问和修改。 https://docs.oracle.com/javase/8/javafx/get-start-tutorial/jfx-architecture.htm#A1107438 任何“实时”场景,即作为窗口一部分的场景,都必须从该线程访问。场景图可以在后台线程中