Java - 多线程程序中的ConcurrentModificationException,Set用synchronized包装

时间:2016-07-05 08:15:09

标签: java multithreading

我在以下代码中崩溃了:

private LocalBundle(Bundle b, Map<Bundle, LocalBundle> clonnedObjs) {
    this();
    synchronized (b.getVariables()) { // b.getVariables() is Set
        addFrom(b, clonnedObjs);
    }
}

但是在addFrom中我得到了崩溃:

private synchronized void addFrom(Bundle b, Map<Bundle, LocalBundle> clonnedObjs) {
    Set<TaintedVariable> variablesOfBundle = b.getVariables();
    for(TaintedVariable v : variablesOfBundle) {

异常消息:

Exception in thread "pool-4-thread-1" java.util.ConcurrentModificationException
    at java.util.HashMap$HashIterator.nextNode(Unknown Source)
    at java.util.HashMap$KeyIterator.next(Unknown Source)
    at x.y.z.LocalBundle.addFrom(VisitedNodesWithBundle.java:198)

有人知道为什么会这样吗?我用synchronized (b.getVariables())包装,但看起来两个线程正在执行for(TaintedVariable v : variablesOfBundle)

2 个答案:

答案 0 :(得分:3)

关于synchronized要记住的事情是,如果访问共享资源的每个人也同步,它只保证独占访问。

例如,您可以同步对共享变量的访问:

synchronized (foo) {
  foo.setBar();
}

您可以认为您拥有对它的独占访问权。但是,没有任何东西可以阻止另一个线程在没有synchronized块的情况下执行某些操作:

foo.setBar();  // No synchronization first.

根据你的描述,听起来似乎正在发生;但是,你没有提供“流氓”线程正在做什么的任何细节。

如果只是在集合中添加元素,则可以在创建集合时使集合同步:

Collections.synchronizedSet(new HashSet<TaintedVariable>());

现在,对该集上方法的单独调用将被同步,因此您将不再获得CME。但请注意,同步应用每个方法调用:如果您进行一系列调用(例如,如果set包含foo然后添加bar),则需要明确synchronized阻止了这种逻辑。

答案 1 :(得分:2)

我不确定,原因是另一个主题。仔细查看javadoc此异常。它说:

  

请注意,此异常并不总是表示某个对象已被另一个线程同时修改。如果单个线程发出违反对象合同的一系列方法调用,则该对象可能会抛出此异常。例如,如果线程在使用失败快速迭代器迭代集合时直接修改集合,则迭代器将抛出此异常。

这意味着,如果您在迭代时尝试修改集合,则可能会在单个线程中导致此类异常。

您必须检查代码是否尝试修改此for循环中的variablesOfBundle集合

for(TaintedVariable v : variablesOfBundle)