我正在看这段代码。此构造函数委托给本机方法“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或更糟糕的
答案 0 :(得分:7)
此构造函数委托给本机方法“System.arraycopy”
实际上,它会在toArray()
上调用someCollection
。如果System.arraycopy
是someCollection
,那么最终会调用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
本身是线程安全的,是的。因此,如果它是CopyOnWriteArrayList
,synchronizedList
或Vector
,如果它不是线程安全的,例如,如果它是另一个ArrayList
,那么你就不行了。 (会发生什么可能比ConcurrentModificationException
更糟。)
原因是ArrayList
构造函数只对其他集合进行了一次原子调用 - 对其toArray
方法。因此它基本上享受方法本身具有的任何线程安全保证。它并不总是像这样实现,但现在正是出于这个原因。我们用ImmutableList.copyOf
在番石榴做同样的事情。
答案 3 :(得分:1)
ConcurrentModificationException不是线程安全的唯一标志。例如,在方法#1中,someCollection也是一个ArrayList,您将永远不会有ConcurrentModificationException(请参阅代码)。但是,数据完整性存在风险 - 如果源ArrayList在复制期间被更改,则只有一些更改可能会反映在副本中(并不一定是最早的更改!)。
换句话说,不保证原子性(除非源是专门为它设计的,例如使用CopyOnWriteArrayList)。
编辑:实际上,假设您没有正确地同步线程,使用一个线程复制数组而另一个线程更新其中的引用在Java内存模型中存在问题,理论上可能导致不可预测的行为。