6
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具体来说,当同步列表提供对列表的线程安全访问时,我不清楚为什么在第二种情况下需要。

4

3 回答 3

6

如果您不锁定迭代,如果另一个线程在循环期间修改它,您将收到 ConcurrentModificationException。

同步所有方法并不能阻止这一点。

这(以及许多其他事情)Collections.synchronized*是完全没用的原因。
您应该使用java.util.concurrent. (并且您应该仔细考虑如何保证您的安全)

作为一般经验法则:

对每个方法都加锁不足以使某些东西成为线程安全的。

有关更多信息,请参阅我的博客

于 2012-08-29T16:06:41.013 回答
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 
于 2012-08-29T16:19:53.393 回答
2

由于同步列表的实现方式,第二个代码需要同步。这在 javadoc 中有解释:

用户在迭代返回的列表时必须手动同步它

两个代码片段的主要区别在于add操作的效果:

  • synchronizedList.get(..)使用同步列表,您可以获得可见性保证:例如,如果其他线程调用,它们将看到新添加的项目。
  • 使用 ArrayList,其他线程可能不会立即看到新添加的项目 - 他们实际上可能永远不会看到它们。
于 2012-08-29T16:20:50.387 回答