【问题标题】:is for-each iteration thread safe?for-each 迭代线程安全吗?
【发布时间】:2014-03-18 20:59:52
【问题描述】:

我在多线程环境中遇到一种情况,没有逻辑问题,但我担心 java Set 的迭代器是否会在某些极端情况下中断。我不知道这是否会在一段时间后崩溃:

class Bar{
    AtomicLong a = 0;
    AtomicLong b = 0;

    public void addA(int value){
        a.addAndGet(value);
    }
    public void addB(int value){
        b.addAndGet(value);
    }
}

class Foo{
    Set<Bar> bars = new CopyOnWriteArraySet();

    public void addBarData(Bar bar){
        if (!bars.add(bar)){
            for (Bar b: bars){
                if (bar.equals(b)){
                    b.addA(bar.a);
                    b.addB(bar.b);
                }
            }
        }
    }
}

问题可能是如果两个线程在不久的时间调用 addBarData(barInstance),一个正在进入 ifs 主体,并开始迭代 for-each,但另一个此时将另一个元素添加到集合中。 for循环(或迭代器)是否会因为元素数量的变化而崩溃?

【问题讨论】:

标签: java multithreading for-loop iterator


【解决方案1】:

不,for-each 循环只是一种编译器魔法,相当于使用Iterator 进行集合迭代和数组索引。它不会添加任何其他效果,例如线程安全。

【讨论】:

  • 你的意思是它不会崩溃,还是会崩溃?我正在使用 CopyOnWriteArraySet,它会保护它免于崩溃吗?
  • 只有在修改集合时才会发生崩溃。您的示例中似乎没有任何内容在修改集合,因此它不会崩溃。如果某些东西正在修改集合,您将需要使用锁定或使用写时复制或类似集合进行限制。
  • 但是你不能修改你遍历的集合。
【解决方案2】:

您问题中的代码看起来是线程安全的,因为:

  • Bar 类使用原子和线程安全的 AtomicLong
  • CopyOnWriteArraySet 也是线程安全的(它会在每次写入操作时复制整个数组)。

所以这一切都是线程安全的,你不会得到ConcurrentModificationException,而且这段代码的结果是可以预测的,因为你迭代的集合实际上是不可变的。

【讨论】:

  • 我担心迭代随着新元素增长的集合(其他线程可能会添加新元素)
  • 目前其他线程可以添加新元素,但只有在添加操作结束后开始迭代的线程才可见。如果您需要立即查看新元素,则不能使用 CopyOnWriteArraySet,但是(例如 'LinkedHashSet). Using LinkedHashSet` 您还需要添加同步块。
  • 这个答案似乎暗示线程安全是可组合的。它不是。如果 a(X)、b(Y) 和 c(X) 都是线程安全的方法,那么方法 d() 只会调用 a(x)、b(y) 和 c(x)仍然可能不是是线程安全的。 a(x) 和 c(x) 方法可能保证让 x 处于“安全”状态,无论在其他线程中发生什么,对于 b(y) 也是如此,但 d() 的“安全”可能意味着保留一个x和y之间的重要关系。在没有互斥的情况下,当同时从多个线程调用时,它可能无法做到这一点。
  • 当然线程安全是不可组合的。此代码是线程安全的,除非有人更改了对 AtomicLongs 的引用。
猜你喜欢
  • 2011-04-15
  • 2012-12-18
  • 2014-12-12
  • 1970-01-01
  • 1970-01-01
  • 2015-05-21
  • 1970-01-01
  • 1970-01-01
  • 2011-05-29
相关资源
最近更新 更多