我正在阅读有关包装器实现的java官方doc,这是用于获取同步集合的集合中的静态方法,例如:List<Type> list = Collections.synchronizedList(new ArrayList<Type>());
...
我不明白的是以下内容(我引用了java doc):
以这种方式创建的集合每个位都是线程安全的,作为正常同步的集合,例如Vector。 面对并发访问,用户在迭代时对返回的集合进行手动同步势在必行。原因是迭代是通过对集合的多次调用来完成的,集合必须组成一个原子操作......
如何作为线程安全的每一位在迭代时需要手动同步?
答案 0 :(得分:2)
在某种意义上它是线程安全的,它的每个方法都是线程安全的,但是如果你对集合执行复合操作,那么你的代码就有可能出现并发问题。
例如:
List<String> synchronizedList = Collections.synchronizedList(someList);
synchronizedList.add(whatever); // this is thread safe
单个方法add()
是线程安全的,但如果我执行以下操作:
List<String> synchronizedList = Collections.synchronizedList(someList);
if(!synchronizedList.contains(whatever))
synchronizedList.add(whatever); // this is not thread safe
if-then-add操作不是线程安全的,因为在whatever
检查后,某些其他线程可能已将contains()
添加到列表中。
答案 1 :(得分:1)
这里没有矛盾:从synchronizedXyz
返回的集合会遇到与直接可用的同步集合相同的缺点,即需要在迭代集合时手动同步。
外部迭代的问题无法通过更好的类设计来解决,因为迭代集合本身就需要对其方法进行多次调用(see this Q&A for detailed explanation)。
请注意,从Java 1.8开始,您可以使用同步集合 * 的forEach
方法进行迭代而无需其他同步。这是线程安全的,并带来额外的好处; see this Q&A for details
这与外部迭代不同的原因是集合中的forEach
实现负责为您同步迭代。