【问题标题】:Why sync again in synchronized collection in interation?为什么在交互中的同步集合中再次同步?
【发布时间】:2013-02-16 22:09:02
【问题描述】:

在 Java 文档中它说同步的集合必须在迭代中再次手动同步。这是为什么?由于返回的集合已经同步。不太明白为什么会这样。谢谢你。

 Collection c = Collections.synchronizedCollection(myCollection);
      ...
    synchronized(c) {
        Iterator i = c.iterator(); // Must be in the synchronized block
        while (i.hasNext())
          foo(i.next());
   }

【问题讨论】:

    标签: java multithreading synchronized


    【解决方案1】:

    原因是迭代操作不能保持链表上的锁。例如,i.hasNext() 将锁定列表,直到检查 hasNext() 为止,但在您真正调用 next() 之前,列表可能会再次更改

    因此,您必须自己锁定列表,以保持整个迭代同步,而不仅仅是单独的每个操作。

    【讨论】:

    • “迭代操作无法保持链表上的锁”,是不是因为迭代操作不是原子的,而是一系列的进程?如果多个线程尝试add()某个东西到收藏?
    • 每个方法调用都是原子的,但迭代作为一个整体是由许多方法调用组成的,所以除非你自己做同步,否则它不可能是原子的。
    【解决方案2】:

    仅同步单个方法。如果允许代码的其他部分在您在迭代器中进行的调用之间调用这些方法,则会破坏列表的完整性,在这种情况下同步变得毫无价值。

    可能有一些方法不需要同步块就可以使用,列表使用synchronizedCollection 包装(例如检查列表中有多少元素),但如果您使用列表并且一种方法取决于另一个被调用方法的精确结果,两个方法调用周围都需要一个同步块,以确保在这些调用之间没有其他东西可以触及列表的状态。

    【讨论】:

    • 那么Collections提供的同步集合有什么意义呢?如果线程 A 需要首先“添加”一个元素,则需要检查它的“大小”,而线程 B 也在访问集合,可能会添加或删除。这种情况下,线程A是否也需要在本地同步集合呢?
    • 你可以在不使用任何其他方式的情况下检查集合的大小(本地方法同步就足够了)。恰当的例子:一个用户界面元素,显示游戏中有多少玩家,而不需要查看集合内部的实际玩家。
    • 在您的用例中,如果线程 B 只是添加或删除一个元素,而线程 A 在 add then size 检查周围有一个同步块,则没有问题。但是,如果没有同步块,线程 B 可能会在大小检查之前添加/删除元素,这将是有问题的。同步集合包装器允许线程 B 不需要显式指定任何同步块的情况。
    • 此外,如果没有包装器,并且在从不同线程调用方法时没有任何互斥,则可以从多个线程同时访问代码,这些线程并非旨在从多个线程同时执行(例如,可能有代码使用本地字段进行缓存)。也可以提出ConcurrentModificationException
    【解决方案3】:

    想象iterator 循环在两个不同的线程中运行。一个线程调用i.next().remove() 而另一个线程在同一个list 上循环是完全合法的,这可能会破坏列表中数据的一致性。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-02-13
      • 1970-01-01
      • 1970-01-01
      • 2010-11-18
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多