【问题标题】:Using for each syntax with Collections.synchronizedSet将每个语法与 Collections.synchronizedSet 一起使用
【发布时间】:2010-02-15 03:08:26
【问题描述】:

问题是这样的。我做了一套

Set<User> users = Collections.synchronizedSet(new HashSet<User>())
...
for(User u : users){
   //do something with u
} 

现在,根据 Java 文档。

用户必须 手动同步返回 迭代时排序集或 它的任何子集、头集或尾集 意见。

 SortedSet s = Collections.synchronizedSortedSet(new HashSortedSet());
 ...
 synchronized(s) {
    Iterator i = s.iterator(); // Must be in the synchronized block
    while (i.hasNext())
       foo(i.next());
 }

我很确定每个语法都使用迭代器,但我不确定是否应该用同步块包装每个循环。

另一件事,我的 IDE (IntelliJ IDEA) 不断报告说,在非最终字段上使用同步块不太可能具有有用的语义,因为不同的线程可能是不同的对象,即使在处理同一个对象时也是如此。

【问题讨论】:

  • 不知道,我是从文档中复制的。这可能是一个遗物的例子。

标签: java collections synchronization


【解决方案1】:
  1. 我是否应该包装每个 for 循环... 不,你不需要用同步块包装每个 for 循环,事实上你甚至不应该这样做,因为它带有性能损失。您只需要包装那些迭代可能由多个线程同时访问的集合的 for 循环。

  2. 我的 IDE (IntelliJ IDEA) 不断报告.... 在您的特定情况下,这不是问题,因为如果不同的线程有不同的 s 实例,那么显然迭代器会也要与众不同。

【讨论】:

    【解决方案2】:

    文档明确指出您应该面对并发访问。它还提供了一个很好的理由为什么应该这样做:

    面对并发访问,用户在迭代返回的集合时必须手动同步它。原因是迭代是通过对集合的多次调用来完成的,集合必须组合成一个原子操作。以下是遍历包装器同步集合的习惯用法。

    http://java.sun.com/docs/books/tutorial/collections/implementations/wrapper.html

    要了解“同步块中的最终变量”的更多信息,请查看:Final variable and synchronized block in java

    【讨论】:

      【解决方案3】:

      由于 for each 在幕后使用迭代器,如果您的 Set 在结构上被修改,您仍然会在处理 for each 期间受到 ConcurrentModificationException 的约束。

      关于 IDE 提供的警告,如果您要重新分配 s,则在尝试在 s 上进行同步时,不同的线程可能会看到不同的对象。在这里使用volatile 应该会有所帮助。

      【讨论】:

        猜你喜欢
        • 2023-04-11
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-03-09
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多