我正在处理我正在进行的项目的问题。有一个主循环运行程序,每个帧调用update()
方法。
我有一个World
的对象,其中包含HashMap<ChunkPos, Chunk>
,其中ChunkPos
是一个数据结构,其xyz位置代表Chunk
在World
中的位置
我试图在我的更新函数中遍历我的世界中的所有块,因为每个Chunk都有自己的GameObject
的ArrayList,其中包含了这个世界中的所有对象。
public void update() {
HashMap<ChunkPos, Chunk> chunkMap = world.getChunkMap();
Iterator<Map.Entry<ChunkPos, Chunk>> it = chunkMap.entrySet().iterator();
while(it.hasNext()) {
Map.Entry<ChunkPos, Chunk> item = it.next();
ArrayList<GameObject> chunkObjects = item.getValue().getObjects();
for(GameObject obj : chunkObjects) {
obj.update();
}
}
}
但是,当我运行该程序时,我在此行收到一个ConcurrentModificationException
:
Map.Entry<ChunkPos, Chunk> item = it.next();
我已经尝试了多种不同的迭代HashMap的方法,但每次我都会遇到同样的错误。我究竟做错了什么?
如果它有帮助,这是完整的StackTrace:
Exception in thread "main" java.util.ConcurrentModificationException
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.anzanama.lwjgl3d.Game.Game.update(Game.java:40)
at com.anzanama.lwjgl3d.Game.Game.loop(Game.java:31)
at com.anzanama.lwjgl3d.Main.main(Main.java:15)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:497)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:144)
只是想在这里做一个说明:我最终最终实现了一个WorldChangeScheduler,它可以安排在世界迭代并更新后对世界进行更改。这使得它更加强大,并且具有额外的好处,我可以稍后返回并将这些WorldChanges转换为其他人使用我的引擎可以挂钩的事件。
这里ConcurrentModificationException
意味着在迭代期间修改了地图而不使用迭代器。
要么obj.update();
修改world.getChunkMap()
地图,要么你有另一个线程在当前void update()
方法的迭代过程中同时添加或删除同一个地图的元素。
因为在显示的代码中没有对地图进行任何修改。
好吧,这段代码造成了麻烦
ArrayList<GameObject> chunkObjects = item.getValue().getObjects();
for(GameObject obj : chunkObjects) {
obj.update();
}
使用迭代器并调用更新
Iterator<GameObject> iter = item.getValue().getObjects().iterator();
while (iter.hasNext()) {
GameObject obj = iter.next();
obj.update();
}
HashMap不是线程安全的,因此在迭代时,当其他线程发生结构修改时,迭代器会快速失败。
通过使用ConcurrentHashMap替换Map实现,您将防止此类问题,但锁定将应用于更新操作,这将影响其性能。如果期望可预测数量的线程同时更新此Map,则可以使用ConcurrentHashMap的concurrencyLevel参数配置并发级别思想。