Многопоточность JavaFX ListChangeListener

Я пытаюсь разработать приложение JavaFX для имитации некоторой системы лифтов.

Каждый объект Elevator работает в своем собственном потоке, и я хочу отображать каждый Elevator.toString() в виде списка. Проблема в том, что после запуска приложения некоторое время я получаю NullPointerException или IndexOutOfBounds от одного или нескольких потоков лифта. Я создал ObservableList of Elevators, где я прослушиваю любые изменения свойств, затем у меня есть еще один ObservableList строки типа, который подключен к listView. Второй обновляется каждый раз, когда прослушиватель изменений запускает wasUpdated() из первого списка.

final ObservableList<String> elevators = FXCollections.observableArrayList();

    final ObservableList<Elevator> obsList = FXCollections.observableArrayList(
            new Callback<Elevator, Observable[]>() {
                @Override
                public Observable[] call(Elevator param) {
                    return new Observable[]{
                            param.getCurrentFloorProp(),
                            param.getDirProp(),
                            param.getSmallSchedule(),
                            param.getDoorsProp(),
                            param.getStatusProp()
                    };
                }
            }
    );

    obsList.addListener(new ListChangeListener<Elevator>() {
        @Override
        public void onChanged(Change<? extends Elevator> c) {
            c.reset();
            while (c.next()) {
                if (c.wasUpdated()) {
                        Platform.runLater(() -> {
                            elevators.set(c.getFrom(), obsList.get(c.getFrom()).toString());
                        });
                }
            }
        }
    });

При попытке исправить это у меня есть две мысли: либо ChangeListener запускает больше изменений, чем Platform.runLater успевает обработать, либо что-то в той же строке, что и https://stackoverflow.com/a/31414801/9696324. Однако я не могу заставить предложенные решения работать, обычно это происходит после 2-3 минут запуска программы без каких-либо проблем.

Когда возникает первая ошибка, графический интерфейс обычно зависает (хотя программа все еще работает хорошо), а затем начинает спамить те же ошибки.

Любые мысли или указатели будут очень признательны, спасибо.

Свойства объявлены в конструкторе лифта:

currentFloor = new SimpleIntegerProperty(INITIAL_FLOOR);
    smallSchedule = new SimpleStringProperty("");
    status = new SimpleBooleanProperty(true);
    dir = new SimpleStringProperty("S");
    doors = new SimpleStringProperty("Doors closed");

И следующие геттеры и сеттеры:

public StringProperty getSmallSchedule() {
    return smallSchedule;
}
public StringProperty getDoorsProp() {
    return doors;
}
public StringProperty getDirProp() {
    return dir;
}
public IntegerProperty getCurrentFloorProp() {
    return currentFloor;
}
public BooleanProperty getStatusProp() {
    return status;
} 

public void setCurrentFloor(int floorNr) { 
    currentFloor.set(floorNr);
}
public void setStatus(boolean status) {
    this.status.set(status);
}
public void setDir(char dir) {
    this.dir.set(Character.toString(dir));
}
public void setSmallSchedule() {
    String temp = "";
    for(int i = 0; i < travelSchedule.size()-1;i++) {
        if(travelSchedule.get(i)==INT_MAX || i > 2)
            break;
        temp += " " + travelSchedule.get(i);
    }
    smallSchedule.set(temp);
}
public void doors(){
    try {
        Thread.sleep(doorOpen);
        doors.set("Doors open");
        Thread.sleep(doorWait);
        checkMBox();
        Thread.sleep(doorClose);
        doors.set("Doors closed");
    } 
    catch (InterruptedException e) {
        e.printStackTrace();
    }
}

person Cob    schedule 02.12.2018    source источник
comment
Что ты будешь делать со своим ObservableList<String> elevators? Если вы планируете поместить его в ListView, TableView или ComboBox, правильный способ сделать это — использовать исходный ObservableList<Elevator>.   -  person VGR    schedule 02.12.2018
comment
Предоставьте минимально воспроизводимый пример, демонстрирующий проблему. И придерживайтесь соглашений об именах, в частности соглашений fx о свойствах и методах получения/установки их значений. ф.и. если вы предоставляете свойство с именем currentFloor, метод получения свойства должен быть currentFloorProperty, а методы доступа к значению get/setCurrentFloor   -  person kleopatra    schedule 02.12.2018
comment
Привет! Спасибо за попытку помочь, но я решил эту проблему, удалив прослушиватель и просто обновляя графический интерфейс каждые 0,1 секунды, этого более чем достаточно. Я думаю, проблема заключалась в том, что он не мог обрабатывать столько обратных вызовов от слушателя.   -  person Cob    schedule 03.12.2018