尝试清除JavaFX ObservableMap时出现java ConcurrentException
我正在向JavaFX8应用程序添加一些加速器,当我尝试清除场景中的加速器时,我遇到了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()方法,调用super并添加所需的新加速器。 然而,有时,当enableAccelerators尝试场景的clear accelerators时,会抛出以下异常:
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);
}
});
}
我怎样才能避免这个问题
# 1 楼答案
您可能需要在
Platform.runLater()
调用中输入代码来更新加速器。试试看如果FX应用程序线程在处理事件时调用您的代码,那么可以想象(正如您所建议的)它在遍历加速器集合时会这样做。使用
Platform.runLater()
将导致在处理FX应用程序线程上当前挂起的任何内容后执行代码(如果这看起来有点像黑客行为,那么,被指控有罪……)
# 2 楼答案
我已经解决了避免移除元素的问题。现在,我没有删除映射的元素,而是将值设为null: