我需要使ArrayLists的ArrayList线程安全。我也无法让客户对集合进行更改。不可修改的包装器是否会使线程安全?或者我需要在集合上使用两个包装器吗?
答案 0 :(得分:10)
这取决于。包装器只会阻止对它包装的集合的更改,而不是对集合中的对象的更改。如果您有ArrayList的ArrayList,则需要单独包装全局List及其每个元素列表,并且您可能还必须对这些列表的内容执行某些操作。最后,您必须确保原始列表对象不会更改,因为包装器仅阻止通过包装器引用而不是原始对象进行更改。
在这种情况下,您不需要同步包装器。
答案 1 :(得分:5)
关于相关主题 - 我看过几个回复建议使用同步收集以实现线程安全。 使用集合的同步版本并不会使其“线程安全” - 尽管每个操作(插入,计数等)在组合两个操作时都受到互斥锁的保护,但不能保证它们会以原子方式执行。 例如,以下代码不是线程安全的(即使使用同步队列):
if(queue.Count > 0)
{
queue.Add(...);
}
答案 2 :(得分:2)
不可修改的包装器仅阻止更改它适用的列表结构。如果此列表包含其他列表,并且您有线程尝试修改这些嵌套列表,那么您不会受到并发修改风险的保护。
答案 3 :(得分:1)
通过查看收藏源,看起来不可修改不使其同步。
static class UnmodifiableSet<E> extends UnmodifiableCollection<E>
implements Set<E>, Serializable;
static class UnmodifiableCollection<E> implements Collection<E>, Serializable;
同步类包装器中有一个互斥对象来执行同步部分,所以看起来你需要同时使用它们来获取两者。或者自己滚动!
答案 4 :(得分:1)
我相信因为UnmodifiableList包装器将ArrayList存储到final字段,所以包装器上的任何读取方法都会看到列表构建时的列表,只要在创建包装器后未修改列表,并且只要包装器内的可变ArrayLists未被修改(包装器无法保护)。
答案 5 :(得分:1)
根据定义,不可变对象是线程安全的(假设没有人保留对原始集合的引用),因此同步不是必要的。
使用Collections.unmodifiableList()包装外部ArrayList 防止客户端更改其内容(从而使其成为线程 安全),但内部ArrayLists仍然是可变的。
使用Collections.unmodifiableList()包装内部ArrayLists 防止客户改变他们的内容(从而使他们 线程安全),这是你需要的。
如果此解决方案导致问题(开销,内存使用等),请告诉我们; 其他解决方案可能适用于您的问题。 :)
编辑:当然,如果列表被修改,它们不是线程安全的。我假设没有进行进一步的编辑。
答案 6 :(得分:1)
如果安全地发布了不可修改的视图,它将是线程安全的,并且在发布不可修改的视图之后永远不会修改可修改的原始文件(包括集合中递归包含的所有对象!)。
如果您想继续修改原始文件,那么您可以创建集合的对象图的防御副本并返回其不可修改的视图,或者使用固有的线程安全列表开始,并返回不可修改的观点。
如果你打算之后仍打算访问未同步的列表,那么你不能返回一个unmodifiableList(synchonizedList(theList));如果多个线程之间共享可变状态,则所有线程在访问该状态时必须在相同的锁上同步。
答案 7 :(得分:0)
如果符合以下条件,这是必要的:
如果您打算仅通过索引从ArrayList读取,则可以认为这是线程安全的。
如有疑问,请选择同步包装。
答案 8 :(得分:0)
不确定我是否明白你要做什么,但我会说大多数情况下的答案都是“不”。
如果你设置了ArrayList的ArrayList和两者,外部和内部列表在创建后永远不会被更改(并且在创建期间只有一个线程可以访问内部和外部列表),它们可能是包装器的线程安全(如果两个,外部和内部列表都以这样的方式包装,即无法修改它们)。 ArrayLists上的所有只读操作很可能是线程安全的。但是,Sun并不保证它们是线程安全的(也不是用于只读操作),因此即使它现在可能正常工作,它也可能在未来中断(如果Sun创建一些内部的话)例如,缓存数据以便更快地访问。