问题是这个。我做了一套
Set<User> users = Collections.synchronizedSet(new HashSet<User>())
...
for(User u : users){
//do something with u
}
现在,根据Java文档。
用户势在必行 手动同步返回 迭代它时排序的集合 任何subSet,headSet或tailSet 视图。
SortedSet s = Collections.synchronizedSortedSet(new HashSortedSet()); ... synchronized(s) { Iterator i = s.iterator(); // Must be in the synchronized block while (i.hasNext()) foo(i.next()); }
我很确定每个语法使用迭代器,但我不确定是否应该使用synchronized块为每个循环包装每个语句。
另一件事,我的IDE(IntelliJ IDEA)继续报告说,在非最终字段上使用synchronized块不太可能具有有用的语义,因为不同的线程可能是不同的对象,即使在处理同一个对象时也是如此。
答案 0 :(得分:3)
文档清楚地表明,你应该在面对并发访问时这样做。这也是你应该这样做的一个很好的理由:
面对并发访问时,用户必须在迭代时手动同步返回的集合。原因是迭代是通过对集合的多次调用来完成的,集合必须组成一个单独的原子操作。以下是迭代包装器同步集合的习惯用法。
http://java.sun.com/docs/books/tutorial/collections/implementations/wrapper.html
要了解更多“同步块中的最终变量”,请看一下:Final variable and synchronized block in java
答案 1 :(得分:3)
我是否应该为每个for循环包装... 不,你不需要用同步块包装每个for循环,事实上你甚至不应该这样做,因为它伴随着性能损失。您需要仅包含那些迭代可能同时由多个线程访问的集合的循环。
我的IDE(IntelliJ IDEA)不断报告.... 在您的特定情况下,这不是问题,因为如果不同的线程有不同的s实例,那么显然迭代器将会也有所不同。
答案 2 :(得分:1)
由于每个人都使用了Iterator,如果你的ConcurrentModificationException
在结构上被修改,那么在处理a Set
期间,你仍需要s
。
关于IDE提供的警告,如果要重新分配s
,则尝试在volatile
上同步时,不同的线程可能会看到不同的对象。使用{{1}}应该有帮助。