2010-04-21 86 views
8

我在看这段代码。此构造委托给本地方法“System.arraycopy”爪哇 - ArrayList的构造函数的线程安全

难道是线程安全的?我的意思是它可以抛出一个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

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

原因是ArrayList构造函数只对其他集合 - 对其toArray方法进行单个原子调用。所以它基本上享有该方法本身具有的任何线程安全保证。它并不总是这样实施,但现在正是因为这个原因。我们在Guava做同样的事情,ImmutableList.copyOf

7

此构造委托给

实际上,它调用toArray()someCollection本地方法“System.arraycopy”。如果someCollectionArrayList,那么最终将会拨打System.arraycopy。对于其他集合类型,数组将以其他方式创建。

它是线程安全的?

而且我的意思是它可以不断抛出ConcurrentModificationException?

如果是ArrayList它不会抛出ConcurrentModificationException ... 但是这并不能使它线程安全的!

例如,如果不同的线程上someCollection调用set(obj, pos),而你的线程调用此构造,那么你的新创建的内容ArrayList是不可预测的。

1

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

换句话说,原子不能保证(除非源为它设计的,如用的CopyOnWriteArrayList)。

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

+0

这不是一个理论问题:不可预知的行为将会发生。唯一的不确定性是*它会发生的频率。 – 2010-04-21 10:25:05

+0

@Stephen:你说得对,但是复制无序写错误是非常困难的。我在以下文章中只找到一个示例:http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html – 2010-04-21 11:22:40

+0

难以重现!=理论。此外,网页中描述的几个例子!=理论。 :-) – 2010-04-21 11:41:12

5

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

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

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

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