19

我需要使 ArrayList 的 ArrayLists 线程安全。我也不能让客户对收藏进行更改。不可修改的包装器会使其线程安全,还是我需要在集合上使用两个包装器?

4

9 回答 9

10

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

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

于 2008-09-17T21:54:55.327 回答
5

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

if(queue.Count > 0)
{
   queue.Add(...);
}
于 2008-09-18T09:44:09.243 回答
2

不可修改的包装器仅防止对其应用的列表结构进行更改。如果此列表包含其他列表,并且您有线程试图修改这些嵌套列表,那么您将无法避免并发修改风险。

于 2008-09-17T21:52:17.830 回答
1

从查看 Collections 源,看起来 Unmodifiable 不会使其同步。

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

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

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

于 2008-09-17T21:53:53.753 回答
1

我相信,因为 UnmodifiableList 包装器将 ArrayList 存储到最终字段,所以包装器上的任何读取方法都会看到该列表,因为它是在构造包装器时,只要在创建包装器后未修改列表,并且作为只要包装器内的可变 ArrayLists 没有被修改(包装器无法防止)。

于 2008-09-17T23:51:55.967 回答
1

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

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

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

让我们知道此解决方案是否会导致问题(开销、内存使用等);其他解决方案可能适用于您的问题。:)

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

于 2008-09-18T19:54:30.693 回答
1

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

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

如果您之后仍打算访问未同步的列表,则不能返回不可修改的列表(同步列表(列表));如果可变状态在多个线程之间共享,则所有线程在访问该状态时必须在相同的锁上同步。

于 2008-09-21T13:08:47.733 回答
0

这是必要的,如果:

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

如果您打算仅按索引从 ArrayList 中读取,则可以假设这是线程安全的。

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

于 2008-09-17T21:58:12.997 回答
0

不确定我是否理解您要做什么,但我会说大多数情况下的答案是“不”。

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

于 2008-09-17T21:59:13.803 回答