【问题标题】:understanding java's synchronized collections了解java的同步集合
【发布时间】:2018-03-26 10:59:03
【问题描述】:

我正在阅读 java 官方 doc 关于 wrappers 的实现,它们是 Collections 中用于获取同步集合的静态方法,例如:List<Type> list = Collections.synchronizedList(new ArrayList<Type>()); ...
我不明白的是以下内容(我引用了 java doc):

以这种方式创建的集合与通常同步的集合(例如 Vector)一样,每一点都是线程安全的。 面对并发访问,用户在迭代返回的集合时必须手动同步。原因是迭代是通过对集合的多次调用来完成的,它必须组合成一个原子操作......

如何在迭代时每一点都是线程安全的需要手动同步??

【问题讨论】:

  • 这意味着当迭代一个正常同步的集合(例如 Vector)时,您还必须手动同步返回的集合。

标签: java collections java.util.concurrent


【解决方案1】:

它是线程安全的,因为它的每个单独的方法都是线程安全的,但是如果您对集合执行复合操作,那么您的代码就有并发问题的风险。

例如:

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 操作不是线程安全的,因为在检查contains() 之后,某些其他线程可能已将whatever 添加到列表中。

【讨论】:

  • 如果您仅通过例如迭代 synchronizedList 的项目,甚至会出现非线程安全问题。 for(String s : synchronizedList) {System.out.prin(s);}。在迭代期间,另一个线程可以编辑列表 -> ConcurrentModificationException
【解决方案2】:

这里没有矛盾:从synchronizedXyz 返回的集合与您直接可用的同步集合存在相同的缺点,即需要在迭代集合时手动同步。

外部迭代的问题无法通过更好的类设计来解决,因为迭代集合本质上需要对其方法进行多次调用 (see this Q&A for detailed explanation)。

请注意,从 Java 1.8 开始,您可以使用同步集合的 forEach 方法进行迭代而无需额外同步*。这是线程安全的,并带来了额外的好处; see this Q&A for details.

这与外部迭代不同的原因是集合内的forEach 实现负责为您同步迭代。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-08-06
    • 2010-11-18
    • 1970-01-01
    相关资源
    最近更新 更多