使用JavaFX进行多线程处理会导致java.lang.IllegalStateException:不在FX应用程序线程上

时间:2016-11-16 19:12:16

标签: java multithreading javafx

所以我得到了一个相当简单的JavaFX应用程序,在按下开始按钮时创建并启动2个线程。我还有一个重置按钮,它应该中断线程并清除GUI组件。这似乎工作正常。然后我应该能够按下重置按钮然后再次启动按钮创建2个线程并再次启动它们。这也有效。当这些线程正在执行时,它们正在使用我的回调,将字符串返回给我的Controller类,然后将其添加到listview中。这在我第一次运行应用程序时工作正常,但如果我按清除然后启动,则在向列表视图添加数据时回调会引发错误。 java.lang.IllegalStateException: Not on FX application thread

据我所知,每当除了main之外的其他一些线程尝试更改fx元素时,都会抛出此内容。但是为什么我第一次点击开始时工作正常,但是没有点击清除然后重新开始?这是我的代码:

/**
 * Controls GUI and start threads..
 * 
 *
 */
public class Controller implements Initializable {
    @FXML
    private Button btnRun;

    @FXML
    private Button btnReset;

    @FXML
    private TextArea taInput;

    @FXML
    private ListView<String> lwProducer;

    @FXML
    private ListView<String> lwConsumer;

    private boolean sync;
    private Buffer buffer;
    private ObservableList<String> conData;
    private ObservableList<String> proData;
    private Thread consumerThread;
    private Thread producerThread;
    private Runnable producerR;
    private Runnable consumerR;

    /**
     * Initializes elements, sets an initial value for the textbox.
     */
    public void initialize(URL location, ResourceBundle resources) {
        taInput.setText("The quick brown\nfox jumps over\nthe lazy dog");
        btnReset.setDisable(true);
        btnRun.setDefaultButton(true);
        lwProducer.setStyle("-fx-focus-color: transparent;");
        lwConsumer.setStyle("-fx-focus-color: transparent;");
    }

    /**
     * Eventhandler for btnRun that will start new threads.
     * 
     * @param e
     */
    @FXML
    protected void btnRun(ActionEvent e) {
        System.out.println(sync);
        conData = FXCollections.observableArrayList();
        proData = FXCollections.observableArrayList();

        buffer = new Buffer();

        producerR = new Producer(new ProducerCB(), buffer, taInput.getText());
        consumerR = new Consumer(new ConsumerCB(), buffer);

        producerThread = new Thread(producerR);
        consumerThread = new Thread(consumerR);

        producerThread.start();
        consumerThread.start();

        btnRun.setDisable(true);
        taInput.setDisable(true);
        btnReset.setDisable(false);
    }

    /**
     * Eventhandler for btnReset. If pressed, threads and all gui components
     * will be cleared.
     * 
     * @param e
     */
    @FXML
    protected void btnReset(ActionEvent e) {
        System.out.println("Resetting");
        producerThread.interrupt();
        consumerThread.interrupt();
        producerThread = null;
        consumerThread = null;
        lwProducer.getItems().clear();
        lwConsumer.getItems().clear();
        conData = null
        proData = null
        producerR = null;
        consumerR = null;
        btnRun.setDisable(false);
        taInput.setDisable(false);
        btnReset.setDisable(true);
    }

    /**
     * Callback for producer thread. Updates the left hand listview with added
     * data.
     * 
     *
     */
    private class ProducerCB implements Callback {
        @Override
        public void returnData(String str) {
            proData.add(str);
            lwProducer.setItems(proData);
        }
    }

    /**
     * Callback for consumer thread. Updates the right hand listview with read
     * data.
     * 
     */
    private class ConsumerCB implements Callback {
        @Override
        public void returnData(String str) {
            conData.add(str); // Throws exception!!!!!!!
            lwConsumer.setItems(conData);
        }
    }
}

堆栈跟踪

Exception in thread "Thread-7" java.lang.IllegalStateException: Not on FX application thread; currentThread = Thread-7
    at com.sun.javafx.tk.Toolkit.checkFxUserThread(Toolkit.java:236)
    at com.sun.javafx.tk.quantum.QuantumToolkit.checkFxUserThread(QuantumToolkit.java:423)
    at javafx.scene.Parent$2.onProposedChange(Parent.java:367)
    at com.sun.javafx.collections.VetoableListDecorator.setAll(VetoableListDecorator.java:113)
    at com.sun.javafx.collections.VetoableListDecorator.setAll(VetoableListDecorator.java:108)
    at com.sun.javafx.scene.control.skin.LabeledSkinBase.updateChildren(LabeledSkinBase.java:575)
    at com.sun.javafx.scene.control.skin.LabeledSkinBase.handleControlPropertyChanged(LabeledSkinBase.java:204)
    at com.sun.javafx.scene.control.skin.ListCellSkin.handleControlPropertyChanged(ListCellSkin.java:49)
    at com.sun.javafx.scene.control.skin.BehaviorSkinBase.lambda$registerChangeListener$61(BehaviorSkinBase.java:197)
    at com.sun.javafx.scene.control.MultiplePropertyChangeListenerHandler$1.changed(MultiplePropertyChangeListenerHandler.java:55)
    at javafx.beans.value.WeakChangeListener.changed(WeakChangeListener.java:89)
    at com.sun.javafx.binding.ExpressionHelper$SingleChange.fireValueChangedEvent(ExpressionHelper.java:182)
    at com.sun.javafx.binding.ExpressionHelper.fireValueChangedEvent(ExpressionHelper.java:81)
    at javafx.beans.property.StringPropertyBase.fireValueChangedEvent(StringPropertyBase.java:103)
    at javafx.beans.property.StringPropertyBase.markInvalid(StringPropertyBase.java:110)
    at javafx.beans.property.StringPropertyBase.set(StringPropertyBase.java:144)
    at javafx.beans.property.StringPropertyBase.set(StringPropertyBase.java:49)
    at javafx.beans.property.StringProperty.setValue(StringProperty.java:65)
    at javafx.scene.control.Labeled.setText(Labeled.java:145)
    at com.sun.javafx.scene.control.skin.ListViewSkin$2.updateItem(ListViewSkin.java:319)
    at javafx.scene.control.ListCell.updateItem(ListCell.java:471)
    at javafx.scene.control.ListCell.lambda$new$160(ListCell.java:167)
    at javafx.collections.WeakListChangeListener.onChanged(WeakListChangeListener.java:88)
    at com.sun.javafx.collections.ListListenerHelper$Generic.fireValueChangedEvent(ListListenerHelper.java:329)
    at com.sun.javafx.collections.ListListenerHelper.fireValueChangedEvent(ListListenerHelper.java:73)
    at javafx.collections.ObservableListBase.fireChange(ObservableListBase.java:233)
    at javafx.collections.ListChangeBuilder.commit(ListChangeBuilder.java:482)
    at javafx.collections.ListChangeBuilder.endChange(ListChangeBuilder.java:541)
    at javafx.collections.ObservableListBase.endChange(ObservableListBase.java:205)
    at javafx.collections.ModifiableObservableListBase.add(ModifiableObservableListBase.java:155)
    at java.util.AbstractList.add(AbstractList.java:108)
    at assignment2.Controller.addData(Controller.java:163)
    at assignment2.Controller.access$2(Controller.java:162)
    at assignment2.Controller$ConsumerCB.returnData(Controller.java:158)
    at assignment2.Consumer.run(Consumer.java:22)
    at java.lang.Thread.run(Thread.java:745)

2 个答案:

答案 0 :(得分:6)

与AWT事件调度程序线程类似,只能在使用Java FX应用程序线程时更新JavaFx UI组件。

有关此主题的进一步阅读,请参阅here。解决此类问题的一种方法是使用Platform.runLater()

答案 1 :(得分:0)

根据Stacktrace,你在非fx线程中改变一些fx元素的值(称为&#34; Thread-7“,显然这是你创建的匿名线程)。我想你可以在conData.add(str)中设置断点并查看调用层次结构。您可以找到自己的根本原因。