单线程代码上的ConcurrentModificationException

时间:2011-07-07 17:42:07

标签: java

编辑:嗯,我觉得很腼腆。我在看错了构造函数。根据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,基本上是一个复制构造函数,略有修改:它接受绑定映射,并递归遍历调用它的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-constructor,而不是我认为被调用的那个。事实上,根据Kal的回答,这违反了foreach循环并发规则。

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

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

由于

2 个答案:

答案 0 :(得分:12)

使用for循环在迭代数组时修改ArrayList时发生ConcurrentModificationException。

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

以下是API的相关文档 -

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

答案 1 :(得分:0)

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);