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

我需要使ArrayLists的ArrayList线程安全。 我也无法让客户对集合进行更改。 不可修改的包装器是否会使线程安全?或者我需要在集合上使用两个包装器吗?

这取决于。 包装器只会阻止对它包装的集合的更改,而不是对集合中的对象的更改。 如果您有ArrayLists的ArrayList,则需要单独包装全局List及其每个元素列表,并且您可能还必须对这些列表的内容执行某些操作。 最后,您必须确保原始列表对象不会更改,因为包装器仅阻止通过包装器引用而不是原始对象进行更改。

在这种情况下,您不需要同步包装器。

在一个相关的主题 – 我已经看到几个回复建议使用同步收集,以实现线程安全。 使用集合的同步版本并不会使其“线程安全” – 尽管每个操作(插入,计数等)在组合两个操作时都受到互斥锁的保护,但无法保证它们将以primefaces方式执行。 例如,以下代码不是线程安全的(即使使用同步队列):

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

不可修改的包装器仅阻止更改它适用的列表结构。 如果此列表包含其他列表,并且您有线程尝试修改这些嵌套列表,那么您不会受到并发修改风险的保护。

通过查看Collections源,看起来Unmodifiable不会使它同步。

 static class UnmodifiableSet extends UnmodifiableCollection implements Set, Serializable; static class UnmodifiableCollection implements Collection, Serializable; 

同步类包装器中有一个互斥对象来执行同步部分,因此看起来你需要同时使用它们来获取两者。 或者自己滚!

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

如果安全地发布了不可修改的视图,那么它将是线程安全的,并且在发布不可修改的视图之后永远不会修改可修改的原始文件(包括递归包含在集合中的所有对象!)。

如果您想继续修改原始文件,那么您可以创建集合的对象图的防御副本并返回其不可修改的视图,或者使用固有的线程安全列表开始,并返回不可修改的视图那。

如果您仍打算在之后访问未同步的List, 则无法返回unmodifiableList(synchonizedList(theList)); 如果多个线程之间共享可变状态,则所有线程在访问该状态时必须在相同的锁上同步。

根据定义,不可变对象是线程安全的(假设没有人保留对原始集合的引用),因此不需要同步。

使用Collections.unmodifiableList()包装外部ArrayList可以防止客户端更改其内容(从而使其成为线程安全),但内部ArrayLists仍然是可变的。

使用Collections.unmodifiableList()包装内部ArrayLists也会阻止客户端更改其内容(从而使它们成为线程安全),这正是您所需要的。

如果此解决方案导致问题(开销,内存使用等),请告诉我们; 其他解决方案可能适用于您的问题。 🙂

编辑:当然,如果列表被修改,它们不是线程安全的。 我假设没有进行进一步的编辑。

不确定我是否明白你要做什么,但我会说大多数情况下答案都是“不”。

如果你设置ArrayList的ArrayList和两者,外部和内部列表永远不能在创建后更改(并且在创建期间只有一个线程可以访问内部和外部列表),它们可能是包装器的线程安全(如果两者都是,外部和内部列表以这样的方式包装,即修改它们是不可能的)。 ArrayLists上的所有只读操作很可能是线程安全的。 但是,Sun并不保证它们是线程安全的(也不是用于只读操作),因此即使它现在可能正常工作,它也可能在未来中断(如果Sun创建了一些内部数据缓存以便更快地访问例)。

这是必要的,如果:

  1. 仍有对原始可修改列表的引用。
  2. 可以通过迭代器访问该列表。

如果您打算仅通过索引从ArrayList读取,则可以认为这是线程安全的。

如有疑问,请选择同步包装器。