JavaFX8: исключение при добавлении элементов в ObservableList: ConcurrentModificationException

У меня есть ChoiceBox, в котором перечислены значения месяцев, когда пользователь выбирает значение, он выполняет это лямбда-выражение:

private TableView<IncomeFX> tableIncome;
private ChoiceBox<Month> choiceBoxIncomeMonths;

private ChangeListener<Month> setChoiceBoxIncomeMonthsBehaviour(){
    ChangeListener<Month> months = (ObservableValue<? extends Month> observable, Month oldValue, Month newValue) -> {
            incomesData.clear();
            Year selectedYear = choiceBoxIncomeYears.getSelectionModel().getSelectedItem();
            ObservableList<IncomeFX> temp = incomeManager.getIncomesOf(selectedYear, newValue);
            incomesData.addAll(temp);

    };
    return months;
}

и как я добавляю слушателя:

choiceBoxIncomeMonths.getSelectionModel().selectedItemProperty().addListener(setChoiceBoxIncomeMonthsBehaviour());

когда я нажимаю на поле выбора, я получаю:

Exception in thread "JavaFX Application Thread" java.util.ConcurrentModificationException
at java.util.AbstractList$Itr.checkForComodification(AbstractList.java:386)
at java.util.AbstractList$Itr.next(AbstractList.java:355)
at java.util.AbstractCollection.addAll(AbstractCollection.java:343)
at javafx.collections.ModifiableObservableListBase.addAll(ModifiableObservableListBase.java:99)
at lite.money.ui.MainUI.lambda$1(MainUI.java:160)
at lite.money.ui.MainUI$$Lambda$120/1680764266.changed(Unknown Source)

это указывает на то, что проблема в строке, где я вызываю: addAll (temp) как я могу решить эту проблему ??? Благодарность


person usertest    schedule 23.05.2015    source источник
comment
Опубликуйте MCVE с акцентом на complete. Эти кусочки не говорят о том, что вы делаете за кулисами.   -  person Roland    schedule 23.05.2015


Ответы (2)


Поскольку вы не опубликовали весь код, я предполагаю, что вы запускаете код в другом потоке, который пытается взаимодействовать с данными JavaFX. Когда другой поток попытается сделать это, он выдаст исключение, поскольку только поток JavaFX должен взаимодействовать с данными.

Я не могу дать больше советов, потому что у меня нет полной кодовой базы того, что вы делаете, чтобы действительно сказать «да, в строке X у вас есть поток Y, доступ к местоположению X, когда этого не должно быть».

Возможно, вы добавляете это в другую тему? Вы будете знать приложение лучше, чем я, поскольку у меня больше нет кода, от которого можно было бы отказаться.

person Water    schedule 23.05.2015
comment
извините, но какой код вы хотите, я разместил настройку прослушивателя флажка и лямбда-выражение, которое выполняется при изменении значения selectionBox, оно переходит в базу данных, чтобы получить наблюдаемый список, который содержит классы для отображения в таблица. таблица уже заполнена некоторыми значениями, поэтому я сначала очищаю ее, а затем добавляю новое значение. и нет там никакой другой темы только основной. - person usertest; 23.05.2015
comment
ConcurrentModificationException говорит, что вы одновременно изменяете то, чего не должны делать. Поэтому вы либо используете несколько потоков (возможно, не зная об этом), либо происходит какая-то другая ошибка (что, как я предполагаю, маловероятно). Поэтому без дополнительного кода из вашего приложения, чтобы увидеть, выполняете ли вы несколько потоков или нет, мы не можем дать вам дальнейший совет. - person Water; 23.05.2015
comment
Также, если вы мне не верите: docs. oracle.com/javase/8/docs/api/java/util/ - person Water; 23.05.2015
comment
нет, чувак, я тебе поверил, просто я не знаю, какой еще код добавить, потому что есть много кода пользовательского интерфейса, есть логика и код БД, все, что связано с измененным значением выбора. - person usertest; 23.05.2015
comment
@usertest У меня есть идея, вы можете перейти к строке, которая вызывает ошибку, и прямо перед ней распечатать все потоки? Это может выглядеть так: Set<Thread> threadSet = Thread.getAllStackTraces().keySet(); Thread[] threadArray = threadSet.toArray(new Thread[threadSet.size()]); for (Thread t : threadArray) { if (t.isAlive() && !t.isDaemon()) { System.out.println(t); } } -- Извините, это выглядит как чушь, но я не хочу публиковать это как ответ, так как на самом деле я ни на что не отвечаю. Таким образом, мы можем видеть, есть ли какие-либо другие потоки. JavaFX создает собственный поток. - person Water; 23.05.2015
comment
три потока: Thread[JavaFX-Launcher,5,main] Thread[main,5,main] Thread[Tread приложения JavaFX,5,main]. я не знал - person usertest; 23.05.2015
comment
Два потока JavaFX? Вы создаете приложение дважды? Я предполагаю, что один из этих потоков является преступником. Возможно, два потока каким-то образом привязаны к одной и той же ссылке, и это заставляет один из них писать одновременно с другим... вызывая ошибку? Это стоит исследовать. - person Water; 23.05.2015
comment
хорошо, чувак, я посмотрю, как это решить, когда я это сделаю, я опубликую ответ - person usertest; 23.05.2015

вот как я это решаю, это плохой код, который я знаю, но я не знаю другого решения, мне нужно очистить его дважды, иначе элементы будут добавлены, как если бы я его не очистил, если у вас есть другое решение, я буду рад:

private ChangeListener<Month> setChoiceBoxIncomeMonthsBehaviour(){
    ChangeListener<Month> months = (ObservableValue<? extends Month> observable, Month oldValue, Month newValue) -> {
        if (!lastMonthValuesFired) {
            incomesData.clear();
            Year selectedYear = choiceBoxIncomeYears.getSelectionModel().getSelectedItem();
            ObservableList<IncomeFX> temp = incomeManager.getIncomesOf(selectedYear, newValue);
            ObservableList<IncomeFX> temp2 = FXCollections.observableList(new ArrayList<IncomeFX>());

            for (IncomeFX t : temp) {
                temp2.add(t);
            }
            incomesData.clear();
            incomesData.addAll(temp2);
        }
    };
    return months;
}
person usertest    schedule 24.05.2015