简体   繁体   English

java集合的不可修改的包装器是否使它们对线程安全?

[英]Does the unmodifiable wrapper for java collections make them thread safe?

I need to make an ArrayList of ArrayLists thread safe. 我需要使ArrayLists的ArrayList线程安全。 I also cannot have the client making changes to the collection. 我也无法让客户对集合进行更改。 Will the unmodifiable wrapper make it thread safe or do I need two wrappers on the collection? 不可修改的包装器是否会使线程安全,或者我需要在集合上使用两个包装器?

It depends. 这取决于。 The wrapper will only prevent changes to the collection it wraps, not to the objects in the collection. 包装器只会阻止对它包装的集合的更改,而不是对集合中的对象的更改。 If you have an ArrayList of ArrayLists, the global List as well as each of its element Lists need to be wrapped separately, and you may also have to do something for the contents of those lists. 如果您有ArrayList的ArrayList,则需要单独包装全局List及其每个元素列表,并且您可能还必须对这些列表的内容执行某些操作。 Finally, you have to make sure that the original list objects are not changed, since the wrapper only prevents changes through the wrapper reference, not to the original object. 最后,您必须确保原始列表对象不会更改,因为包装器仅阻止通过包装器引用而不是原始对象进行更改。

You do NOT need the synchronized wrapper in this case. 在这种情况下,您不需要同步包装器。

On a related topic - I've seen several replies suggesting using synchronized collection in order to achieve thread safety. 在一个相关的主题 - 我已经看到几个回复建议使用同步收集,以实现线程安全。 Using synchronized version of a collection doesn't make it "thread safe" - although each operation (insert, count etc.) is protected by mutex when combining two operations there is no guarantee that they would execute atomically. 使用集合的同步版本并不会使其“线程安全” - 尽管每个操作(插入,计数等)在组合两个操作时都受到互斥锁的保护,但不能保证它们会以原子方式执行。 For example the following code is not thread safe (even with a synchronized queue): 例如,以下代码不是线程安全的(即使使用同步队列):

if(queue.Count > 0)
{
   queue.Add(...);
}

The unmodifiable wrapper only prevents changes to the structure of the list that it applies to. 不可修改的包装器仅阻止更改它适用的列表结构。 If this list contains other lists and you have threads trying to modify these nested lists, then you are not protected against concurrent modification risks. 如果此列表包含其他列表,并且您有线程尝试修改这些嵌套列表,那么您不会受到针对并发修改风险的保护。

It will be thread-safe if the unmodifiable view is safely published, and the modifiable original is never ever modified (including all objects recursively contained in the collection!) after publication of the unmodifiable view. 如果安全地发布了不可修改的视图,它将是线程安全的,并且在发布不可修改的视图之后永远不会修改可修改的原始文件(包括递归包含在集合中的所有对象!)。

If you want to keep modifying the original, then you can either create a defensive copy of the object graph of your collection and return an unmodifiable view of that, or use an inherently thread-safe list to begin with, and return an unmodifiable view of that. 如果您想继续修改原始文件,那么您可以创建集合的对象图的防御副本并返回其不可修改的视图,或者使用固有的线程安全列表开始,并返回不可修改的视图那。

You cannot return an unmodifiableList(synchonizedList(theList)) if you still intend to access theList unsynchronized afterwards; 如果您仍打算在之后访问未同步的List, 则无法返回unmodifiableList(synchonizedList(theList)); if mutable state is shared between multiple threads, then all threads must synchronize on the same locks when they access that state. 如果多个线程之间共享可变状态,则所有线程在访问该状态时必须在相同的锁上同步。

From looking at the Collections source, it looks like Unmodifiable does not make it synchronized. 从看集合源,它看起来像不可修改并不能使它同步。

static class UnmodifiableSet<E> extends UnmodifiableCollection<E>
                 implements Set<E>, Serializable;

static class UnmodifiableCollection<E> implements Collection<E>, Serializable;

the synchronized class wrappers have a mutex object in them to do the synchronized parts, so looks like you need to use both to get both. 同步类包装器中有一个互斥对象来执行同步部分,所以看起来你需要同时使用它们来获取两者。 Or roll your own! 或者自己滚!

我相信因为UnmodifiableList包装器将ArrayList存储到final字段,所以包装器上的任何读取方法都会看到列表的构造,只要在创建包装器后未修改列表,并且只要包装器中的可变ArrayLists未被修改(包装器无法保护)。

An immutable object is by definition thread safe (assuming no-one retains references to the original collections), so synchronization is not necessary. 一个不可变的目的是通过定义线程安全(假设没有人保留原始集合的参考文献),所以同步是不必要的。

Wrapping the outer ArrayList using Collections.unmodifiableList() prevents the client from changing its contents (and thus makes it thread safe), but the inner ArrayLists are still mutable. 使用Collections.unmodifiableList()包装外部ArrayList可以防止客户端更改其内容(从而使其成为线程安全),但内部ArrayLists仍然是可变的。

Wrapping the inner ArrayLists using Collections.unmodifiableList() too prevents the client from changing their contents (and thus makes them thread safe), which is what you need. 使用Collections.unmodifiableList()包装内部ArrayLists也会阻止客户端更改其内容(从而使它们成为线程安全),这正是您所需要的。

Let us know if this solution causes problems (overhead, memory usage etc); 如果此解决方案导致问题(开销,内存使用等),请告诉我们; other solutions may be applicable to your problem. 其他解决方案可能适用于您的问题。 :) :)

EDIT: Of course, if the lists are modified they are NOT thread safe. 编辑:当然,如果列表被修改,它们不是线程安全的。 I assumed no further edits were to be made. 我假设没有进行进一步的编辑。

This is neccessary if: 这是必要的,如果:

  1. There is still a reference to the original modifiable list. 仍有对原始可修改列表的引用。
  2. The list will possibly be accessed though an iterator. 可以通过迭代器访问该列表。

If you intend to read from the ArrayList by index only you could assume this is thread-safe. 如果您打算只通过索引从ArrayList读取,则可以认为这是线程安全的。

When in doubt, chose the synchronized wrapper. 如有疑问,请选择同步包装器。

Not sure if I understood what you are trying to do, but I'd say the answer in most cases is "No". 我不确定我是否明白你要做什么,但我会说大多数情况下答案都是“不”。

If you setup an ArrayList of ArrayList and both, the outer and inner lists can never be changed after creation (and during creation only one thread will have access to either inner and outer lists), they are probably thread safe by a wrapper (if both, outer and inner lists are wrapped in such a way that modifying them is impossible). 如果你设置ArrayList的ArrayList和两者,外部和内部列表永远不能在创建后更改(并且在创建期间只有一个线程可以访问内部和外部列表),它们可能是包装器的线程安全(如果两者都是,外部和内部列表以这样的方式包装,即修改它们是不可能的)。 All read-only operations on ArrayLists are most likely thread-safe. ArrayLists上的所有只读操作很可能是线程安全的。 However, Sun does not guarantee them to be thread-safe (also not for read-only operations), so even though it might work right now, it could break in the future (if Sun creates some internal caching of data for quicker access for example). 但是,Sun并不保证它们是线程安全的(也不是用于只读操作),因此即使它现在可能正常工作,它也可能在未来中断(如果Sun创建了一些内部数据缓存以便更快地访问例)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM