Java - ArrayList构造函数的线程安全性

时间:2010-04-21 09:49:22

标签: java collections concurrency

我正在看这段代码。此构造函数委托给本机方法“System.arraycopy”

线程安全吗?我的意思是它可以抛出一个ConcurrentModificationException吗?

public Collection<Object> getConnections(Collection<Object> someCollection) {
    return new ArrayList<Object>(someCollection);
}

如果要复制的集合是ThreadSafe,例如CopyOnWriteArrayList,会有什么不同吗?

public Collection<Object> getConnections(CopyOnWriteArrayList<Object> someCollection) {
    return new ArrayList<Object>(someCollection);
}

编辑: 我知道ThreadSafe!= ConcurrentModificationException。我试图在某个时间点拍摄数据快照。因此,如果另一个线程通过副本中途写入someCollection,我不在乎结果是否有新对象。我只是不希望它抛出ConcurrentModificationException或更糟糕的

4 个答案:

答案 0 :(得分:7)

  

此构造函数委托给本机方法“System.arraycopy”

实际上,它会在toArray()上调用someCollection。如果System.arraycopysomeCollection,那么最终会调用ArrayList。对于其他集合类型,将以其他方式创建数组。

  

线程安全吗?

没有

  

我的意思是它是否会抛出ConcurrentModificationException?

如果它是ArrayList,则不会抛出ConcurrentModificationException ... ,但这不会使其成为线程安全的!

例如,如果在您的线程调用此构造函数时,另一个线程在set(obj, pos)上调用someCollection,则新创建的ArrayList的内容将无法预测。

答案 1 :(得分:5)

线程安全和ConcurrentModificationException是不同的概念。线程安全对象是多个线程可以同时调用其方法的对象,并且保证对象中保存的数据不会被破坏(例如:http://thejavacodemonkey.blogspot.com/2007/08/making-your-java-class-thread-safe.html)。例如,当您正在迭代集合并且集合发生更改时,会发生ConcurrentModificationException。更改可能来自不同的线程或相同的线程。

在构造函数中,如果另一个线程在构造函数复制时更改someCollection,则可能导致未定义的行为(即新集合中的数据损坏,因为集合不是线程安全的),或者一个ConcurrentModificationException(如果集合确实检测到并发修改,但这不能保证,因为它不是线程安全的......: - )

如果构造函数要使用Collection<Object>,则需要确保其他线程在构造函数返回之前不会修改集合。

另一方面,CopyOnWriteArrayList是线程安全的并且保证不会抛出ConcurrentModificationException,所以你应该安全地这样做,而不需要编写额外的同步代码。

答案 2 :(得分:5)

您的问题是,您是否可以使用new ArrayList<Foo>(thatCollection)安全地获取可能正在由另一个线程进行并发修改的集合的快照。答案是:只要thatCollection本身是线程安全的,是的。因此,如果它是CopyOnWriteArrayListsynchronizedListVector,如果它不是线程安全的,例如,如果它是另一个ArrayList,那么你就不行了。 (会发生什么可能比ConcurrentModificationException更糟。)

原因是ArrayList构造函数只对其他集合进行了一次原子调用 - 对其toArray方法。因此它基本上享受方法本身具有的任何线程安全保证。它并不总是像这样实现,但现在正是出于这个原因。我们用ImmutableList.copyOf在番石榴做同样的事情。

答案 3 :(得分:1)

ConcurrentModificationException不是线程安全的唯一标志。例如,在方法#1中,someCollection也是一个ArrayList,您将永远不会有ConcurrentModificationException(请参阅代码)。但是,数据完整性存在风险 - 如果源ArrayList在复制期间被更改,则只有一些更改可能会反映在副本中(并不一定是最早的更改!)。

换句话说,不保证原子性(除非源是专门为它设计的,例如使用CopyOnWriteArrayList)。

编辑:实际上,假设您没有正确地同步线程,使用一个线程复制数组而另一个线程更新其中的引用在Java内存模型中存在问题,理论上可能导致不可预测的行为。