So I got a rather simple JavaFX application that creates and starts 2 threads when pressing a start button. I also have a reset button which should interrupt the threads and clear the GUI components. This seems to work fine. Then I should be able to press the reset button and then start button again to create 2 threads and start them again. This also works. When these threads are executing they are using my callback, to give back string to my Controller class, which then adds it to a listview. This works fine the first time I run the application, but if I press clear and then start, the callback throws error when adding data to the listview. java.lang.IllegalStateException: Not on FX application thread
As I understand this is thrown whenever some other thread than main tries to change a fx element. But why does it work fine the first time I click start, but not after clicking clear and then start again? This is my code:
/**
* 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);
}
}
}
Stacktrace
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)