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

发布于 2024-08-29 20:18:57 字数 706 浏览 3 评论 0原文

我正在看这段代码。这个构造函数委托给本机方法“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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

此刻的回忆 2024-09-05 20:18:57

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

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

线程安全吗?

不。

我的意思是它可以抛出 ConcurrentModificationException 吗?

如果它是一个ArrayList,它不会抛出ConcurrentModificationException ... 但这并不意味着它是线程安全的!!

我正在尝试拍摄某个时间点的数据快照。

您不会(总是)获得一致的快照。

例如,如果另一个线程在您的线程调用此构造函数时对 someCollection 调用 set(obj, pos),则新创建的 ArrayList< 的内容/code> 是不可预测的。

在 Java 11 版本中,ArrayList(Collection) 构造函数对参数集合调用 toArray()。当且仅当 toArray 调用保证提供一致的快照时,生成的 ArrayList 才是集合的一致快照。对于某些集合类(例如CopyOnWriteList)来说确实如此,但一般情况并非如此。

This constructor delegates to the native method "System.arraycopy"

Actually, it calls toArray() on someCollection. That will eventually call System.arraycopy if someCollection is an ArrayList. For other collection types the array will be created in other ways.

Is it Thread safe?

No.

And by that I mean can it ever throw a ConcurrentModificationException?

If it is an ArrayList it won't throw ConcurrentModificationException ... but that does not make it thread-safe!!

I am trying to take a snapshot of data at a point in time.

You won't (always) get a consistent snapshot.

For example, if a different thread calls set(obj, pos) on someCollection while your thread is calling this constructor, then the contents of your newly created ArrayList are unpredictable.

In the Java 11 version, the ArrayList(Collection) constructor calls toArray() on the argument collection. The resulting ArrayList will be a consistent snapshot of the collection if and only if the toArray call is guaranteed to give a consistent snapshot. This is true for some collection classes (for example CopyOnWriteList) but not in general.

泪冰清 2024-09-05 20:18:57

您的问题是,您是否可以安全地获取可能正在由另一个线程使用 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 as thatCollection itself is thread-safe, yes. So if it's a CopyOnWriteArrayList, synchronizedList or Vector, If it's not thread-safe, for example if it's another ArrayList, you're not fine. (What will happen could be worse than a ConcurrentModificationException.)

The reason is that the ArrayList constructor makes only a single atomic call to the other collection -- to its toArray 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 with ImmutableList.copyOf.

那支青花 2024-09-05 20:18:57

线程安全和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.

疯到世界奔溃 2024-09-05 20:18:57

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文