列表中这两个同步用法之间的行为有何不同?

时间:2012-08-29 16:02:58

标签: java concurrency synchronized

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

3 个答案:

答案 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(..),其他线程将看到新添加的项目。
  • 使用ArrayList,其他线程可能无法立即看到新添加的项目 - 实际上他们可能看不到它们。