ConcurrentException при попытке очистить JavaFX ObservableMap

1

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

public abstract class DialogController{

    private Map<KeyCombination, Runnable> accelerators;
    .
    .
    .

    protected final void loadMainPane() throws IOException {
        .
        .
        .
        pane.sceneProperty().addListener(new ChangeListener<Scene>() {
            @Override
            public void changed(ObservableValue<? extends Scene> observableValue, Scene oldScene, Scene newScene) {
                if (newScene != null)
                    enableAccelerators();
                else
                    disableAccelerators(oldScene);
            }
        });
    }

    protected void enableAccelerators() {
        accelerators = new HashMap<KeyCombination, Runnable>(pane.getScene().getAccelerators());
        pane.getScene().getAccelerators().clear();
        pane.getScene().getAccelerators().put(new KeyCodeCombination(KeyCode.ENTER, KeyCombination.ALT_DOWN), new Runnable() {
            @Override
            public void run() {
                submit(null);
            }
        });
    }

    protected void disableAccelerators(Scene scene) {
        scene.getAccelerators().putAll(accelerators);
    }
}

Когда подклассу необходимо добавить новые ускорители, им нужно только переопределить метод enableAccelerators(), вызвать супер и добавить желаемые новые ускорители. Однако иногда, когда enableAccelerators пытаются очистить ускорители сцены, это исключение:

java.util.ConcurrentModificationException: null
at java.util.HashMap$HashIterator.nextNode(HashMap.java:1429)
at java.util.HashMap$EntryIterator.next(HashMap.java:1463)
at java.util.HashMap$EntryIterator.next(HashMap.java:1461)
at com.sun.javafx.collections.ObservableMapWrapper$ObservableEntrySet$1.next(ObservableMapWrapper.java:588)
at com.sun.javafx.collections.ObservableMapWrapper$ObservableEntrySet$1.next(ObservableMapWrapper.java:576)
at com.sun.javafx.scene.KeyboardShortcutsHandler.processAccelerators(KeyboardShortcutsHandler.java:340)
at com.sun.javafx.scene.KeyboardShortcutsHandler.dispatchBubblingEvent(KeyboardShortcutsHandler.java:168)
at com.sun.javafx.event.CompositeEventDispatcher.dispatchBubblingEvent(CompositeEventDispatcher.java:59)
at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:58)
at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
at com.sun.javafx.event.EventUtil.fireEventImpl(EventUtil.java:74)
at com.sun.javafx.event.EventUtil.fireEvent(EventUtil.java:54)
at javafx.event.Event.fireEvent(Event.java:204)
at javafx.scene.Scene$KeyHandler.process(Scene.java:3949)
at javafx.scene.Scene$KeyHandler.access$2100(Scene.java:3896)
at javafx.scene.Scene.impl_processKeyEvent(Scene.java:2036)
at javafx.scene.Scene$ScenePeerListener.keyEvent(Scene.java:2493)
at com.sun.javafx.tk.quantum.GlassViewEventHandler$KeyEventNotification.run(GlassViewEventHandler.java:170)
at com.sun.javafx.tk.quantum.GlassViewEventHandler$KeyEventNotification.run(GlassViewEventHandler.java:123)
at java.security.AccessController.doPrivileged(Native Method)
at com.sun.javafx.tk.quantum.GlassViewEventHandler.handleKeyEvent(GlassViewEventHandler.java:197)
at com.sun.glass.ui.View.handleKeyEvent(View.java:517)
at com.sun.glass.ui.View.notifyKey(View.java:927)
at com.sun.glass.ui.gtk.GtkApplication._runLoop(Native Method)
at com.sun.glass.ui.gtk.GtkApplication.access$200(GtkApplication.java:48)
at com.sun.glass.ui.gtk.GtkApplication$6$1.run(GtkApplication.java:149)
at java.lang.Thread.run(Thread.java:745)

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

protected void enableAccelerators() {
    accelerators = new HashMap<KeyCombination, Runnable>(pane.getScene().getAccelerators());
    synchronized (pane.getScene().getAccelerators()){
        pane.getScene().getAccelerators().clear();
    }
    pane.getScene().getAccelerators().put(new KeyCodeCombination(KeyCode.ENTER, KeyCombination.ALT_DOWN), new Runnable() {
        @Override
        public void run() {
            submit(null);
        }
    });
}

Как я могу избежать этой проблемы?

  • 0
    Вы где-нибудь используете цикл foreach?
  • 0
    @AnthonyBenavente Я не использую foreach здесь.
Показать ещё 2 комментария
Теги:
javafx
javafx-8
concurrentmodification

2 ответа

1
Лучший ответ

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

protected void enableAccelerators() {
    accelerators = new HashMap<KeyCombination, Runnable>(pane.getScene().getAccelerators());
    for (KeyCombination combination : accelerators.keySet())
        accelerators.put(combination, null);
    pane.getScene().getAccelerators().put(new KeyCodeCombination(KeyCode.ENTER, KeyCombination.ALT_DOWN), new Runnable() {
        @Override
        public void run() {
            submit(null);
        }
    });
}
2

Возможно, вам придется поместить код для обновления ускорителей в вызове Platform.runLater(). Пытаться

        public void changed(ObservableValue<? extends Scene> observableValue, Scene oldScene, Scene newScene) {
            Platform.runLater(() -> {
                if (newScene != null)
                    enableAccelerators();
                else
                    disableAccelerators(oldScene);
            });
        }

Если поток приложения FX вызывает код во время обработки событий, он может, как вы полагаете, сделать это, итерации через сборник ускорителей. Использование Platform.runLater() приведет к тому, что ваш код будет выполнен, если после того, как что-то в настоящее время находится на рассмотрении в потоке приложения FX, было обработано.

(Если это кажется немного взломанным, ну, виноватым, как обвинили...)

  • 0
    К сожалению, проблема просто уменьшена, но не устранена.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню