Java 为什么会出现并发修改错误?

Java 为什么会出现并发修改错误?,java,multithreading,thread-safety,lwjgl,Java,Multithreading,Thread Safety,Lwjgl,我有一个名为getChunks()的同步方法。这是在整个程序中调用块集的唯一方法,但是在对它们进行迭代(通过调用getChunks())时,我得到一个concurrentModificationException。这是因为运行在单独线程中的ChunkManager类会生成一个新的块。但是它访问块的唯一方法是通过getChunks()来访问 getChunks()方法: public synchronized Set getChunks(){ 返回块; } 发生异常的render()方法 pub

我有一个名为getChunks()的同步方法。这是在整个程序中调用块集的唯一方法,但是在对它们进行迭代(通过调用getChunks())时,我得到一个concurrentModificationException。这是因为运行在单独线程中的ChunkManager类会生成一个新的块。但是它访问块的唯一方法是通过getChunks()来访问

getChunks()方法:

public synchronized Set getChunks(){
返回块;
}
发生异常的render()方法

public void render() {
    for(WorldChunk wc : chunkMap.getChunks()) { // <-- This is the line where the exception occurs
        wc.render();
    }
}
public void render(){
对于(WorldChunk wc:chunkMap.getChunks()){//worldcunk.CHUNK\u WIDTH+worldcunk.CHUNK\u DEPTH){
wc.unload();
}
}
试一试{
睡眠(块\更新\延迟\毫秒);
}捕捉(中断异常e){
e、 printStackTrace();
}
}
}

编辑 堆栈跟踪:

Exception in thread "main" java.util.ConcurrentModificationException
at java.util.HashMap$HashIterator.nextEntry(HashMap.java:894)
at java.util.HashMap$KeyIterator.next(HashMap.java:928)
at java.util.AbstractCollection.addAll(AbstractCollection.java:333)
at java.util.HashSet.<init>(HashSet.java:117)
at com.ryxuma.kalidus.world.World.render(World.java:35)
at com.ryxuma.kalidus.Core.renderPerspective(Core.java:35)
at org.heatstroke.Heatstroke$Runner.run(Heatstroke.java:365)
at org.heatstroke.Heatstroke.start(Heatstroke.java:124)
at com.ryxuma.kalidus.Core.main(Core.java:60)
线程“main”java.util.ConcurrentModificationException中的异常 位于java.util.HashMap$HashIterator.nextery(HashMap.java:894) 在java.util.HashMap$KeyIterator.next(HashMap.java:928) 位于java.util.AbstractCollection.addAll(AbstractCollection.java:333) 位于java.util.HashSet.(HashSet.java:117) 位于com.ryxuma.kalidus.world.world.render(world.java:35) 位于com.ryxuma.kalidus.Core.renderPerspective(Core.java:35) 位于org.heatstroke.heatstroke$Runner.run(heatstroke.java:365) 位于org.heatstroke.heatstroke.start(heatstroke.java:124) 位于com.ryxuma.kalidus.Core.main(Core.java:60)
获取集合引用的操作是线程安全的,但从那时起,您对该集合所做的任何操作都不是线程安全的。一个简单的解决方案是

  • 返回集合的副本
  • 迭代时锁定集合
  • 使用一个不会像CopyOnWriteArraySet那样抛出CME的集合 (更新时可能会更贵)

编辑:对于那些感兴趣的人,如果你想要一个ConcurrentHashSet,有一个技巧你可以使用

Set <E> set = Collections.newSetFromMap(new ConcurrentHashMap<E, Boolean>());
Set Set=Collections.newSetFromMap(新的ConcurrentHashMap());

你的
wc.unload()方法做什么

如果在对
集合进行迭代时尝试添加/删除该集合中的元素,则可能会收到此错误

看这里:

特别是本部分:

请注意,此异常并不总是表示对象已被其他线程并发修改。如果单个线程发出一系列违反对象约定的方法调用,该对象可能会引发此异常例如,如果线程在使用fail fast迭代器迭代集合时直接修改集合,迭代器将抛出此异常。

对于一个解决方案,我能想到的最简单的方法是跟踪所有要删除的元素,并在使用
Set.removeAll()
完成迭代后删除它们


如果您愿意,请查看
java.util.concurrent
,您可以在其中找到允许您在迭代时进行修改的集合(请注意它们的工作方式,因为这可能不是您所期望的)。

在方法渲染中,某些线程会更新块,通过同步
chunkMap
@MarekSebera可以避免这种情况,但我认为通过同步getChunks()方法,更新块的线程将等待迭代完成,然后再更改您可以发布的任何stacktrace?哪一行抛出异常?@Ryxuma:不;它只是等待该方法返回。它与它返回的对象没有任何关系。的可能副本会将其从哈希映射中删除。但是它被称为同步(?)所以它不重要?是的,它很重要。问题不是修改来自不同线程的数据,而是在迭代数据时修改数据。那么您有什么建议/在答案中添加了建议。当我尝试复制时,会出现一个错误,因为当我迭代该副本以创建新副本时,它已更改。@Ryxuma这意味着它正在更改,而没有先锁定。谢谢!尽管我使用ConcurrentHashMap对其进行了排序:)@Ryxuma如果您想要ConcurrentHashSet,有一个技巧可以使用
Set Set=Collections.newSetFromMap(new ConcurrentHashMap())
Exception in thread "main" java.util.ConcurrentModificationException
at java.util.HashMap$HashIterator.nextEntry(HashMap.java:894)
at java.util.HashMap$KeyIterator.next(HashMap.java:928)
at java.util.AbstractCollection.addAll(AbstractCollection.java:333)
at java.util.HashSet.<init>(HashSet.java:117)
at com.ryxuma.kalidus.world.World.render(World.java:35)
at com.ryxuma.kalidus.Core.renderPerspective(Core.java:35)
at org.heatstroke.Heatstroke$Runner.run(Heatstroke.java:365)
at org.heatstroke.Heatstroke.start(Heatstroke.java:124)
at com.ryxuma.kalidus.Core.main(Core.java:60)
Set <E> set = Collections.newSetFromMap(new ConcurrentHashMap<E, Boolean>());