我的代码中有:
public static Set<Long> workItemsForTasks = new HashSet<Long>();
这是一个Web应用程序,在代码中,用户可以向地图添加新项目 - 我添加如下代码:
WorkflowOperations.workItemsForTasks.add(workItem.getId());
一旦工作流程到达特定代码,我就像这样遍历地图:
Iterator workItemsIter = service.workItemsForTasksToBMS.iterator();
while (workItemsIter.hasNext()) {
workItemsIter.remove();
...
}
我的问题是:
一旦我得到迭代器 - 如果另一个用户向地图添加了一个新项目(因为它不在同一个网页中) - 它会在我循环时影响地图吗?或者检索迭代器可以确保它保持地图大小直到循环开始的时间?
EDITED
这是一个接受不同Web服务调用的Web应用程序。
在呼叫A中,用户可能会添加呼叫B中需要处理的数据。
所以我定义了一个单例类(通过spring bean),它保存了我需要处理的数据集(数据是数字),每次我有Web服务时都会修改Set。一旦用户询问对于Web服务调用B - 我必须收集到目前为止的数据并用它执行一些操作。
答案 0 :(得分:3)
是否从多个线程同时访问该组?
如果这一切都发生在一个线程上,那么你正在做的事情很好。
但是,如果有并发访问,那么你做错了两件事。首先,HashSet
不是线程安全的。其次,在迭代它时修改HashSet
(通过Iterator
除外)是错误的,并且(希望!)会产生ConcurrentModificationException
。
您可以通过考虑java.util.concurrent
中的课程来解决这个问题,或者您也可以在案例中添加一些自己的同步。您可以同步对集合的访问,当您需要从中删除内容时,请立即执行所有操作。
private static void doMyStuff() {
Set<Long> myWorkingSet;
synchronized (workItemsForTasks) {
myWorkingSet = new HashSet(workItemsForTasks);
workItemsForTasks.clear();
}
for (long x : myWorkingSet) {
// do something
}
}
如果您执行此类操作,则应在类中隐藏workItemsForTasks
隐藏访问方法,以确保正确同步。
答案 1 :(得分:2)
静态通常是一件坏事。每当你看到它时,要小心
此代码将在多线程环境中执行,因此您所编写的内容将无效 - 您将获得ConcurrentModificationException
你没有域名模型!多组多头没有意义。尝试用更高层次的抽象来表达正在发生的事情。
你没有关注“告诉不要问”,特别是违反了LoD-F(http://pragprog.com/articles/tell-dont-ask) - 这是一种说明你的代码很快就会很难的方式跟随谁做了什么。
或者,要破解大部分工作的东西,请使用CopyOnWriteArraySet
。
答案 2 :(得分:1)
首先,我假设您说地图时,实际上是Set。根据{{3}},
请注意,此实现未同步。如果有多个线程 同时访问哈希集,以及至少一个线程 修改集合,必须在外部同步。这是 通常通过自然地同步某个对象来完成 封装集合。如果不存在这样的对象,则该集合应该是 使用Collections.synchronizedSet方法“包装”。这是最好的 在创建时完成,以防止意外的不同步访问 集:
HashSet的线程安全实现如下:
Set s = Collections.synchronizedSet(new HashSet(...));
就迭代器而言,在存在不同步的并发修改时不可能做出任何硬保证,因此在迭代时应该手动同步返回的集合,否则结果将是不确定的。
Set workItemsForTasks = Collections.synchronizedSet(new HashSet());
synchronized(workItemsForTasks) {
Iterator workItemsIter = workItemsForTasks.iterator(); //Must be in synchronized block
while (workItemsIter.hasNext())
//Do Something
}