在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<Test> values = new ArrayList<Test>(2); values.add(new Test(1, 2)); values.add(new Test(3, 4)); List<Test> readOnly = Collections.unmodifiableList(values); for (Test t : readOnly) { t.a = 5; } for (Test t : values) { System.out.println(t.a); } } }
输出:
5 5
来自@WMR answser的重要注意事项。
It depends on if the threads that are reading your collection are started before or after you're filling it. If they're started before you fill it, you have no guarantees (without synchronizing), that these threads will ever see the updated values. The reason for this is the Java Memory Model, if you wanna know more read the section "Visibility" at this link: http://gee.cs.oswego.edu/dl/cpj/jmm.html And even if the threads are started after you fill your collection, you might have to synchronize because your collection implementation could change its internal state even on read operations (thanks Michael Bar-Sinai, I didn't know such collections existed). Another very interesting read on the topic of concurrency which covers topics like publishing of objects, visibility, etc. in much more detail is Brian Goetz's book Java Concurrency in Practice.
集合本身不是,但是请记住,如果集合所包含的内容也不是不可变的,则这些单独的类需要它们自己的同步。
我们不必像其他答案中所述。如果要确保收藏集是只读的,可以使用:
yourCollection = Collections.unmodifableCollection(yourCollection);
(对于List,Set,Map和其他集合类型,存在类似的方法)
这取决于正在读取集合的线程是在填充它之前还是之后启动。如果在填充之前就已启动它们,则无法保证(不进行同步),这些线程将永远不会看到更新后的值。
原因是Java内存模型,如果我们想了解更多信息,请阅读以下链接中的"可见性"部分:http://gee.cs.oswego.edu/dl/cpj/jmm.html
并且即使在填充集合后启动线程,我们也可能必须进行同步,因为即使在读取操作时,集合实现也可能会更改其内部状态(感谢Michael Bar-Sinai,我不知道标准JDK中是否存在此类集合。 )。
Brian Goetz的书Java Concurrency in Practice,是有关并发主题的另一个非常有趣的读物,其中涉及对象发布,可见性等主题。
在一般情况下,我们应该这样做。这是因为某些集合在读取期间会更改其内部结构。使用访问顺序的LinkedHashMap是一个很好的例子。但是,不要只相信我的话:
In access-ordered linked hash maps, merely querying the map with get is a structural modification The Linked hash map's javadoc
如果我们完全确定没有缓存,没有集合统计信息,没有优化,也没有有趣的东西,那么我们根本不需要同步。在那种情况下,我会对集合施加类型约束:不要将集合声明为Map(这将允许LinkedHashMap),而应将其声明为HashMap(对于纯粹主义者,是HashMap的最终子类,但也可能会采用它)远的...)。