You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

JavaFX/Gluon多线程wait/notify报错:NOT ON FX APPLICATION THREAD求助

问题分析与解决方案

核心错误原因

JavaFX强制要求所有UI组件的修改操作必须在FX应用线程(UI线程)中执行,你的appThread是自定义后台线程,直接调用label_view.setText()违反了这个规则,导致"NOT ON FX APPLICATION THREAD"报错。

修复步骤

  1. 用Platform.runLater()包装所有UI更新代码,确保它们在FX线程执行
  2. 优化线程同步逻辑,避免潜在死锁或线程安全问题
  3. 修正代码中的拼写错误(如@READOUBLE应为@READDOUBLE)

修改后的代码示例

appThread.java

package com.knn.client;

import com.knnapplication.views.ExampleView;

import java.io.IOException;

import javafx.application.Platform;
import javafx.scene.control.Label;

public class appThread implements Runnable {
    private final Label label_view;
    private final Thread th;
    private final Object lock = new Object();

    public Object getLock() {
        return lock;
    }

    public appThread(Label label) {
        this.label_view = label;
        this.th = new Thread(this);
        th.start();
        System.out.println("Thread start()");
    }

    @Override
    public void run() {
        String risposta = "";
        try {
            ExampleView.getClient().getOut().writeObject(1);
            ExampleView.getClient().getOut().writeObject("servo.dat");
        } catch (IOException e) {
            throw new RuntimeException(e);
        }

        boolean flag = true;
        do {
            try {
                risposta = ExampleView.getClient().getIn().readObject().toString();
            } catch (IOException | ClassNotFoundException e) {
                throw new RuntimeException(e);
            }

            System.out.println(risposta);

            if (!risposta.contains("@ENDEXAMPLE")) {
                if (risposta.equals("@READSTRING")) {
                    // 用Platform.runLater包装UI更新,确保在FX线程执行
                    Platform.runLater(() -> label_view.setText("Inserisci valore discreto: "));
                    synchronized (lock) {
                        try {
                            lock.wait();
                        } catch (InterruptedException e) {
                            Thread.currentThread().interrupt(); // 重置中断状态
                            throw new RuntimeException(e);
                        }
                    }
                } else if (risposta.equals("@READDOUBLE")) { // 修正拼写错误
                    double x = 0.0;
                    do {
                        Platform.runLater(() -> label_view.setText("Inserisci valore continuo"));
                        synchronized (lock) {
                            try {
                                lock.wait();
                            } catch (InterruptedException e) {
                                Thread.currentThread().interrupt();
                                throw new RuntimeException(e);
                            }
                        }
                        // 建议将输入值获取逻辑移至Presenter,避免后台线程依赖UI组件
                    } while (Double.isNaN(x));
                }
            } else {
                flag = false;
            }
        } while (flag);
    }
}

ExamplePresenter.java

package com.knnapplication.views;

import com.gluonhq.charm.glisten.application.AppManager;
import com.gluonhq.charm.glisten.control.AppBar;
import com.gluonhq.charm.glisten.control.TextField;
import com.gluonhq.charm.glisten.mvc.View;
import com.gluonhq.charm.glisten.visual.MaterialDesignIcon;
import com.knn.client.appThread;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.layout.VBox;
import java.io.IOException;

public class ExamplePresenter {
    @FXML private View exampleView;
    @FXML private Button send_sample;
    @FXML private Button start_example;
    @FXML private TextField sample_prompt;
    @FXML private VBox _input_section;
    @FXML private Label label;

    private appThread currentThread;

    @SuppressWarnings("removal")
    public void initialize() {
        exampleView.showingProperty().addListener((obs, oldValue, newValue) -> {
            if (newValue) {
                AppBar appBar = AppManager.getInstance().getAppBar();
                appBar.setNavIcon(MaterialDesignIcon.MENU.button(e ->
                        AppManager.getInstance().getDrawer().open()));
                appBar.setTitleText("Inserisci esempio:");
                // 此处更新UI安全,因为回调在FX线程执行
                label.setText(ExampleView.getFile().toString());
            }
            // 仅在视图首次显示时创建线程,避免重复实例化
            if (newValue && currentThread == null) {
                currentThread = new appThread(label);
            }
        });
    }

    @FXML
    void sendExample(ActionEvent event) throws IOException {
        ExampleView.getClient().getOut().writeObject(sample_prompt.getText().trim());
        synchronized (currentThread.getLock()) {
            currentThread.getLock().notify();
        }
        // 此处更新UI安全,因为事件回调在FX线程执行
        label.setText("Valore inserito correttamente!");
    }

    @FXML
    void startExample(ActionEvent event) throws IOException, ClassNotFoundException, InterruptedException {
        // 可在此处初始化服务器连接或启动线程(若未提前启动)
    }
}

额外优化建议

  • 避免在后台线程中持有UI组件引用,可通过回调或事件通知Presenter更新UI
  • 优先使用JavaFX自带的Task或Service并发工具,它们内置线程管理和UI同步支持
  • 处理线程中断时,务必重置中断状态(Thread.currentThread().interrupt()),防止后续代码忽略中断信号

内容的提问来源于stack exchange,提问作者Luigi Daddario

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.16 02:40:32