gpt4 book ai didi

iterator - 另一个 ConcurrentModificationException 问题

转载 作者:行者123 更新时间:2023-12-04 06:42:11 27 4
gpt4 key购买 nike

我搜索了 StackOverflow 并且有很多 ConcurrentModificationException 问题。读完之后,我还是一头雾水。我得到了很多这样的异常(exception)。我正在使用“注册表”设置来跟踪对象:

public class Registry {
public static ArrayList<Messages> messages = new ArrayList<Messages>();
public static ArrayList<Effect> effects = new ArrayList<Effect>();
public static ArrayList<Projectile> proj = new ArrayList<Projectile>();

/** Clears all arrays */
public static void recycle(){
messages.clear();
effects.clear();
proj.clear();
}
}

我通过像这样访问 ArrayLists 来向这些列表添加和删除对象: Registry.effects.add(obj)Registry.effects.remove(obj)
我设法通过使用重试循环来解决一些错误:
//somewhere in my game..
boolean retry = true;
while (retry){
try {
removeEffectsWithSource("CHARGE");
retry = false;
}
catch (ConcurrentModificationException c){}
}

private void removeEffectsWithSource(String src) throws ConcurrentModificationException {
ListIterator<Effect> it = Registry.effects.listIterator();
while ( it.hasNext() ){
Effect f = it.next();
if ( f.Source.equals(src) ) {
f.unapplyEffects();
Registry.effects.remove(f);
}
}
}

但在其他情况下,这是不切实际的。我的 drawProjectiles() 中不断收到 ConcurrentModificationExceptions方法,即使它没有修改任何内容。我想罪魁祸首是如果我触摸了屏幕,它会创建一个新的 Projectile 对象并将其添加到 Registry.proj 而 draw 方法仍在迭代。

我不能很好地用draw方法做一个重试循环,否则它会重新绘制一些对象。所以现在我不得不找到一个新的解决方案。有没有更稳定的方法来完成我正在做的事情?

哦,我的问题的第 2 部分:很多人建议使用 ListIterators(我一直在使用),但我不明白.. 如果我调用 ListIterator.remove()它是从它正在迭代的 ArrayList 中删除该对象,还是只是从 Iterator 本身中删除它?

最佳答案

顶线,三个建议:

  • 不要做“在循环中包装异常”的事情。异常(exception)是针对异常(exception)情况,而不是控制流。 ( Effective Java #57 或 Exceptions and Control Flow"using exceptions for control flow" 的示例)
  • 如果您要使用 Registry 对象,请在该对象上公开线程安全的行为,而不是访问器方法,并在该单个类中包含并发推理。你的生活会变得更好。 禁止在公共(public)领域暴露藏品 . (ew,为什么这些字段是 static ?)
  • 要解决实际的并发问题,请执行以下操作之一:
  • 使用同步集合(潜在的性能损失)
  • 使用并发集合(有时逻辑复杂,但可能很高效)
  • 使用快照(可能带有 synchronizedReadWriteLock 在封面下)

  • 您问题的第 1 部分

    对于多线程场景,您应该使用并发数据结构,或者使用同步器并制作防御性副本。可能直接将集合公开为 public字段是错误的:您的注册表应该向这些集合公开线程安全的行为访问器。例如,也许你想要一个 Registry.safeRemoveEffectBySource(String src)方法。将线程细节保留在注册表内部,这似乎是您设计中此聚合信息的“所有者”。

    因为你可能真的不需要 List语义,我建议将这些替换为 ConcurrentHashMaps 包裹到 Set使用 Collections.newSetFromMap() .

    您的 draw()方法可以 a) 使用 Registry.getEffectsSnapshot()返回集合快照的方法;或 b) 使用 Iterable<Effect> Registry.getEffects()返回安全可迭代版本的方法(可能只是由 ConcurrentHashMap 支持,在任何情况下都不会抛出 CME)。我认为 (b) 在这里更可取,只要绘制循环不需要修改集合。这在 mutator 线程和 draw() 之间提供了非常弱的同步保证。线程,但假设 draw()线程运行得足够频繁,错过更新或其他可能没什么大不了的。

    您问题的第 2 部分

    作为另一个答案,在单线程情况下,您应该确保使用 Iterator.remove()删除该项目,但同样,您应该将此逻辑包装在 Registry 中如果可能的话,上课。在某些情况下,您需要锁定一个集合,对其进行迭代以收集一些聚合信息,并在迭代完成后进行结构修改。你问 remove()方法只是将它从 Iterator 中删除或来自支持集合...参见 API contract for Iterator.remove() 它告诉您它从基础集合中删除对象。另见 SO question .

    关于iterator - 另一个 ConcurrentModificationException 问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4102850/

    27 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com