代码之家  ›  专栏  ›  技术社区  ›  WolfmanDragon

Java集合的不可修改包装是否使线程安全?

  •  19
  • WolfmanDragon  · 技术社区  · 16 年前

    我需要使arraylist的arraylist线程安全。我也不能让客户端更改集合。不可修改的包装会使其线程安全吗?还是在集合中需要两个包装?

    9 回复  |  直到 16 年前
        1
  •  10
  •   Michael Borgwardt    16 年前

    这要看情况而定。包装器将只阻止对其包装的集合进行更改,而不阻止对集合中的对象进行更改。如果您有一个arraylist的arraylist,那么全局列表及其每个元素列表都需要单独包装,并且您可能还需要为这些列表的内容做一些事情。最后,您必须确保原始列表对象没有更改,因为包装器只阻止通过包装器引用而不是原始对象进行更改。

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

        2
  •  5
  •   Jason Plank Maksim Kondratyuk    13 年前

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

    if(queue.Count > 0)
    {
       queue.Add(...);
    }
    
        3
  •  2
  •   Dan Dyer    16 年前

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

        4
  •  1
  •   John Gardner    16 年前

    从集合源来看,它看起来是不可修改的。 使其同步。

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

    同步类包装器中有一个互斥对象来完成同步部分,所以看起来您需要同时使用这两个对象来获取这两个部分。或者自己滚!

        5
  •  1
  •   djpowell    16 年前

    我相信,因为unmodifiableList包装器将arraylist存储到最后一个字段,所以包装器上的任何读取方法都将看到构建包装器时的列表,只要在创建包装器后不修改列表,并且只要包装器中的可变arraylist不被修改(包装器不能反对)

        6
  •  1
  •   Chris Vest    16 年前

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

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

    不能 如果以后仍打算访问未同步的列表,则返回一个不可修改的列表(synchonizedlist(thelist));如果在多个线程之间共享可变状态,则 全部的 线程必须在上同步 相同的 当他们访问该状态时锁定。

        7
  •  1
  •   volley    13 年前

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

    使用Collections.UnmodifiableList()包装外部ArrayList 防止客户端更改其内容(从而使其成为线程 但是内部数组列表仍然是可变的。

    使用Collections.UnmodifiableList()包装内部ArrayList 阻止客户端更改其内容(从而使其 线程安全),这是您需要的。

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

    编辑:当然,如果修改了列表,它们就不是线程安全的。我以为不会再做进一步的修改了。

        8
  •  0
  •   Mecki    16 年前

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

    如果您设置了arraylist的arraylist,并且两者都设置,则在创建之后,外部和内部列表将永远无法更改(并且在创建期间,只有一个线程可以访问内部和外部列表),它们可能是由包装器进行线程安全保护的(如果外部和内部列表都被包装在这样一种方式中,则无法修改它们)。对arraylist的所有只读操作都很可能是线程安全的。然而,太阳并没有 保证 它们是线程安全的(也不适用于只读操作),因此即使它现在可以工作,将来也可能会中断(例如,如果Sun为更快的访问创建了一些内部数据缓存)。

        9
  •  0
  •   Jason Plank Maksim Kondratyuk    13 年前

    如果出现以下情况,这是必要的:

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

    如果您只打算按索引从数组列表中读取,那么可以假定这是线程安全的。

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