Java - ArrayList 构造函数的线程安全
我正在看这段代码。这个构造函数委托给本机方法“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 或更糟的情况
I am looking at this piece of code. This constructor delegates to the native method "System.arraycopy"
Is it Thread safe? And by that I mean can it ever throw a ConcurrentModificationException?
public Collection<Object> getConnections(Collection<Object> someCollection) {
return new ArrayList<Object>(someCollection);
}
Does it make any difference if the collection being copied is ThreadSafe eg a CopyOnWriteArrayList?
public Collection<Object> getConnections(CopyOnWriteArrayList<Object> someCollection) {
return new ArrayList<Object>(someCollection);
}
Edit:
I am aware that ThreadSafe != ConcurrentModificationException. I am trying to take a snapshot of data at a point in time. Therefore if another Thread writes to someCollection midway thru the copy I dont care if the result has the new object or not. I just dont want it to throw a ConcurrentModificationException or worse
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
实际上,它在
someCollection
上调用toArray()
。如果someCollection
是ArrayList
,最终将调用System.arraycopy
。对于其他集合类型,将以其他方式创建数组。不。
如果它是一个
ArrayList
,它不会抛出ConcurrentModificationException
... 但这并不意味着它是线程安全的!!您不会(总是)获得一致的快照。
例如,如果另一个线程在您的线程调用此构造函数时对
someCollection
调用set(obj, pos)
,则新创建的ArrayList< 的内容/code> 是不可预测的。
在 Java 11 版本中,
ArrayList(Collection)
构造函数对参数集合调用toArray()
。当且仅当toArray
调用保证提供一致的快照时,生成的 ArrayList 才是集合的一致快照。对于某些集合类(例如CopyOnWriteList
)来说确实如此,但一般情况并非如此。Actually, it calls
toArray()
onsomeCollection
. That will eventually callSystem.arraycopy
ifsomeCollection
is anArrayList
. For other collection types the array will be created in other ways.No.
If it is an
ArrayList
it won't throwConcurrentModificationException
... but that does not make it thread-safe!!You won't (always) get a consistent snapshot.
For example, if a different thread calls
set(obj, pos)
onsomeCollection
while your thread is calling this constructor, then the contents of your newly createdArrayList
are unpredictable.In the Java 11 version, the
ArrayList(Collection)
constructor callstoArray()
on the argument collection. The resultingArrayList
will be a consistent snapshot of the collection if and only if thetoArray
call is guaranteed to give a consistent snapshot. This is true for some collection classes (for exampleCopyOnWriteList
) but not in general.您的问题是,您是否可以安全地获取可能正在由另一个线程使用
new ArrayList(thatCollection)
进行并发修改的集合的快照。答案是:只要thatCollection
本身是线程安全的,就可以。因此,如果它是 CopyOnWriteArrayList、synchronizedList 或 Vector,如果它不是线程安全的,例如如果它是另一个 ArrayList >,你不太好。 (将会发生的情况可能比 ConcurrentModificationException 更糟糕。)原因是 ArrayList 构造函数仅对另一个集合(对其
)进行一次原子调用。 toArray 方法。因此,它本质上享有该方法本身所具有的任何线程安全保证。它并不总是这样实施的,但现在正是出于这个原因。我们在 Guava 中使用 ImmutableList.copyOf 做同样的事情。
Your question is whether you can safely get a snapshot of a collection that might be undergoing concurrent modification by another thread using
new ArrayList<Foo>(thatCollection)
. The answer is: as long asthatCollection
itself is thread-safe, yes. So if it's aCopyOnWriteArrayList
,synchronizedList
orVector
, If it's not thread-safe, for example if it's anotherArrayList
, you're not fine. (What will happen could be worse than aConcurrentModificationException
.)The reason is that the
ArrayList
constructor makes only a single atomic call to the other collection -- to itstoArray
method. So it essentially enjoys whatever thread-safety guarantees that method itself has. It was not always implemented like this, but is now for just this reason. We do the same thing in Guava withImmutableList.copyOf
.线程安全和ConcurrentModificationException是不同的概念。线程安全对象是多个线程可以同时调用其方法的对象,并且保证对象中保存的数据不会被损坏(例如:http://thejavacodemonkey.blogspot.com/2007/08/making-your-java-class-thread-safe。 html)。例如,当您正在迭代集合并且集合发生更改时,就会发生 ConcurrentModificationException。更改可能来自不同的线程,也可能来自同一线程。
在构造函数中,如果另一个线程在构造函数复制时更改了
someCollection
,则可能会导致未定义的行为(即新集合中的数据损坏,因为集合不是线程安全的),或者ConcurrentModificationException(如果集合确实检测到并发修改,但这不能保证,因为它不是线程安全的...:-)如果您的构造函数要采用
Collection
另一方面,CopyOnWriteArrayList 是线程安全的,并且保证不会抛出 ConcurrentModificationException,因此您应该安全地这样做,而无需编写额外的同步代码。
Thread safe and ConcurrentModificationException are different concepts. A thread safe object is one where multiple threads can call its methods at the same time, and the data held within the object is guaranteed not to become corrupted (example: http://thejavacodemonkey.blogspot.com/2007/08/making-your-java-class-thread-safe.html). A ConcurrentModificationException occurs when, for example, you are in the middle of iterating through a collection, and the collection changes. The change might come from a different thread, or the same thread.
In your constructor, if another thread changes the
someCollection
while your constructor is copying it, it could result either in undefined behaviour (i.e. data corruption in your new collection, because the collections are not thread safe), or a ConcurrentModificationException (if the collection does detect the concurrent modification, but this is not guaranteed, because it is not thread safe... :-)If your constructor is going to take a
Collection<Object>
, you would need to ensure that other threads do not modify the collection until after your constructor returns.On the other hand, a CopyOnWriteArrayList is thread safe and guarantees not to throw ConcurrentModificationException, so you should be safe doing it this way, without writing extra synchronization code.
ConcurrentModificationException 并不是线程不安全的唯一标志。例如,如果在方法 #1 中 someCollection 也是一个 ArrayList,则永远不会出现 ConcurrentModificationException(请参阅代码)。但是,数据完整性存在风险 - 如果源 ArrayList 在复制过程中发生更改,则只有部分更改可能会反映在副本中(不一定是最旧的更改!)。
换句话说,无法保证原子性(除非源是专门为其设计的,例如使用 CopyOnWriteArrayList)。
编辑:实际上,假设您没有正确同步线程,使用一个线程复制数组,而另一个线程更新其中的引用在 Java 内存模型中是有问题的,理论上可能会导致不可预测的行为。
ConcurrentModificationException is not the only sign of something being not thread safe. If for example in method #1 someCollection is also an ArrayList, you will never have a ConcurrentModificationException (see the code). However, data integrity is at risk - if the source ArrayList is altered during its copying, only some of the changes may be reflect in the copy (and not necessarily the oldest changes!).
In other words, atomicity is not guaranteed (unless the source is specifically designed for it, such as with CopyOnWriteArrayList).
EDIT: actually, assumning that you don't synchronize your threads properly, copying an array with one thread while another thread updates references in it is problematic in the Java Memory Model, and theoretically can result in unpredictable behavior.