List<String> list = new ArrayList<String>();
list.add("a");
...
list.add("z");
synchronized(list) {
Iterator<String> i = list.iterator();
while(i.hasNext()) {
...
}
}
和
List<String> list = new ArrayList<String>();
list.add("a");
...
list.add("z");
List<String> synchronizedList = Collections.synchronizedList(list);
synchronized(synchronizedList) {
Iterator<String> i = synchronizedList.iterator();
while(i.hasNext()) {
...
}
}
具体来说,当同步列表提供对列表的线程安全访问时,我不清楚为什么在第二个实例中需要synchronized
。
答案 0 :(得分:6)
如果你没有锁定迭代,如果另一个线程在循环期间修改它,你将得到一个ConcurrentModificationException。
同步所有方法并不能防止这种情况发生。
这(以及许多其他事情)是Collections.synchronized*
完全没用的原因
您应该使用java.util.concurrent
中的类。 (你应该仔细考虑如何保证你的安全)
作为一般经验法则:
对每个方法进行拍打锁定不足以使线程安全。
有关更多信息,请参阅my blog
答案 1 :(得分:3)
synchronizedList
仅使每次调用都成为原子。在您的情况下,循环进行多次调用,因此在每次调用/迭代之间,另一个线程可以修改列表。如果您使用其中一个并发集合,则不会出现此问题。
查看此集合与ArrayList的不同之处。
List<String> list = new CopyOnWriteArrayList<String>();
list.addAll(Arrays.asList("a,b,c,d,e,f,g,h,z".split(",")));
for(String s: list) {
System.out.print(s+" ");
// would trigger a ConcurrentModifcationException with ArrayList
list.clear();
}
即使重复清除列表,它也会打印以下内容,因为在创建迭代器时会删除内容。
a b c d e f g h z
答案 2 :(得分:2)
由于实现了同步列表的方式,需要同步第二个代码。这解释为in the javadoc:
当迭代时,用户必须手动同步返回的列表
两个代码段之间的主要区别在于add
操作的效果:
synchronizedList.get(..)
,其他线程将看到新添加的项目。