在Java中,如果它是只读的,我是否需要声明我的集合是否已同步?

我的J2EE webapp启动时,我一次填充一个集合。 然后,几个线程可以同时访问它,但只能读取它。

我知道使用同步集合对于并行写入是强制性的,但是我仍然需要它来进行并行读取吗?

通常不会,因为在这种情况下您不会更改集合的内部状态。 迭代集合时,会创建迭代器的新实例,迭代的状态是每个迭代器实例。


除了注意:请记住,通过保留只读集合,您只能阻止对集合本身的修改。 每个集合元素仍然可以更改。

class Test { public Test(final int a, final int b) { this.a = a; this.b = b; } public int a; public int b; } public class Main { public static void main(String[] args) throws Exception { List values = new ArrayList(2); values.add(new Test(1, 2)); values.add(new Test(3, 4)); List readOnly = Collections.unmodifiableList(values); for (Test t : readOnly) { ta = 5; } for (Test t : values) { System.out.println(ta); } } } 

这输出:

 5 5 

@WMR回答者的重要注意事项。

这取决于读取集合的线程是在填充之前还是之后启动的。 如果它们在您填充之前已经启动,则您无法保证(没有同步),这些线程将看到更新的值。

原因是Java内存模型,如果您想了解更多内容,请阅读此链接中的“可见性”部分: http : //gee.cs.oswego.edu/dl/cpj/jmm.html

即使在填充集合后启动线程,您也可能需要进行同步,因为即使在读取操作中,您的集合实现也可能会更改其内部状态(感谢Michael Bar-Sinai ,我不知道存在此类集合)。

关于并发主题的另一个非常有趣的读物,包括对象的发布,可见性等更详细的主题,是Brian Goetz的书“ Java Concurrency in Practice” 。

这取决于读取集合的线程是在填充之前还是之后启动的。 如果它们在您填充之前已经启动,则您无法保证(没有同步),这些线程将看到更新的值。

原因是Java内存模型,如果您想了解更多内容,请阅读此链接中的“可见性”部分: http : //gee.cs.oswego.edu/dl/cpj/jmm.html

即使在填充集合后启动线程,您也可能需要进行同步,因为即使在读取操作中,您的集合实现也可能会更改其内部状态(感谢Michael Bar-Sinai ,我不知道标准JDK中存在此类集合)。

关于并发主题的另一个非常有趣的读物,包括对象的发布,可见性等更详细的主题,是Brian Goetz的书“ Java Concurrency in Practice” 。

在一般情况下,你应该。 这是因为某些集合在读取期间会更改其内部结构。 使用访问顺序的LinkedHashMap就是一个很好的例子。 但是,不要只听我的话:

在访问顺序链接哈希映射中,仅使用get查询映射是结构修改链接哈希映射的javadoc

如果您完全确定没有缓存,没有收集统计​​信息,没有优化,也没有任何有趣的东西 – 您不需要同步。 在这种情况下,我会在集合上放置一个类型约束:不要将集合声明为Map(允许LinkedHashMap),而是将HashMap声明为HashMap(对于纯粹主义者来说,是HashMap的最后一个子类,但也可能是远…)。

您不必像其他答案中所解释的那样。 如果您想确保您的collections是只读的,您可以使用:

 yourCollection = Collections.unmodifableCollection(yourCollection); 

(List,Set,Map和其他集合类型存在类似的方法)

集合本身没有,但要记住,如果它所拥有的也不是不可变的,那些单独的类需要它们自己的同步。