繁体   English   中英

单线程代码上的 ConcurrentModificationException

[英]ConcurrentModificationException on single-threaded code

编辑:嗯,我觉得很害羞。 我在看错误的构造函数。 根据 Kal 的回答,被调用的真正构造函数(见下文) - 违反了 foreach 循环的并发规则。

无论如何,感谢您的帮助。 它仍然可以帮助我修复代码的实际错误。

全部

我是一个相当新的 Java 程序员,我才刚刚开始掌握该语言的基本句柄。 我目前正在使用对话参与者系统,但首先尝试使我们的系统对逻辑术语的表示符合规范。 我快完成了,但遇到了以下错误:

Exception in thread "main" java.util.ConcurrentModificationException
at java.util.AbstractList$Itr.checkForComodification(AbstractList.java:372)
at java.util.AbstractList$Itr.next(AbstractList.java:343)
at com.Term.<init>(Term.java:97)
at com.Term.substituteVariables(Term.java:251)
at com.Term.substituteVariables(Term.java:247)
at com.Term.substituteVariables(Term.java:247)
at com.TermPredTestArch.main(TermPredTestArch.java:40)

有问题的方法,substituteVariables,基本上是一个复制构造函数,稍作修改:它接受一个 map 绑定,并递归遍历它被调用的 Term Object,一路找到变量并将它们换成它们实例化。 奇怪的是,昨晚我离开时它似乎还在工作(虽然我没有进行广泛的测试),但现在拒绝玩得很好; 我没有做任何实质性的修改。

相关代码如下(232-252行):

232  /** Returns a new Term with the appropriate bindings substituted */
233  public Term substituteVariables(Map<Variable, Symbol> bindings) {
234    ArrayList<Symbol> args    = this.getArgs();
235    ArrayList<Symbol> newArgs = new ArrayList<Symbol>();
236    Set<Variable> vars        = this.getVars();
237    Set<Variable> bindingKeys = bindings.keySet();
238    for(Symbol s: args) {
239      // if s is a Variable, check to see if it has a substituion, and
240      // if so, swap it out
241      if(s instanceof Variable) {
242        if(bindingKeys.contains(s)) newArgs.add(bindings.get(s));
243        else                        newArgs.add(s);
244      // if s is a Term, add it and recursively substitute any variables
245      // it has within the current set of bindings
246      } else if(s instanceof Term) {
247        newArgs.add(((Term) s).substituteVariables(bindings));
248      // if s is just a symbol, simply add it to the args
249      } else newArgs.add(s);
250    }
251    return new Term(this.getName(), newArgs);
252  }

编辑:这是 Term 的构造函数:

public Term(String n, ArrayList<Symbol> a) {
    super(n);
    args = a;
    HashSet<Variable> varsToAdd = new HashSet<Variable>();
    for(Symbol s: a) parseArg(s.toString());
}          

那是被调用的-actual-构造函数,而不是我认为被调用的构造函数。 根据 Kal 的回答,这确实违反了 foreach 循环并发规则。

根据我已经完成的研究,我知道 ConcurrentModificationException 通常是由多个线程在没有同步的情况下同时迭代/修改集合引起的,但是我在这里没有故意的并行性,也没有在 class 或使用它的测试代码中的其他任何地方. 否则,我不完全确定。 class 的 javadoc 提到它也可能是由迭代器同时迭代和修改集合引起的,但我认为我也没有这样做; 我只是观察迭代的集合并使用其中的信息来构建另一个集合。 这是否也违反了并发规则?

任何你们都可以提供的指针将不胜感激。 对于任何严重违反 Java 礼仪或风格的行为,我也会先发制人地道歉(也请随时指出这些!)。

谢谢

当您修改 ArrayList 并使用 for 循环遍历它时,会发生 ConcurrentModificationException。

正确执行此操作的方法是使用迭代器添加/删除方法。

这是来自 API 的相关文档——

此类的 iterator 和 listIterator 方法返回的迭代器是快速失败的:如果在创建迭代器后的任何时间对 list 进行结构修改,除了通过迭代器自己的 remove 或 add 方法之外的任何方式,迭代器将抛出 ConcurrentModificationException。 因此,面对并发修改,迭代器快速而干净地失败,而不是在未来不确定的时间冒任意的、非确定性的行为。

newArgs.add(((Term) s).substituteVariables(bindings));

因此,在这部分代码中,您会遇到错误; 就像 Kal 说的那样,它发生在您编辑您正在迭代的 ArrayList 时。 修复问题更改

for(Symbol s: args) {

for (int i = 0; i < args.size(); i++) {
    Symbol s = args.get(i);

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM