我有一个有状态EJB,它调用Web无法解析Web分析页面的方法。
这是我的有状态代码:
@Override
public void parse() {
while(true) {
if(false == _activeMode) {
break;
}
for(String url : _urls){
if(false == _activeMode) {
break;
}
for(String prioritaryUrl : _prioritaryUrls) {
if(false == _activeMode)
break;
boursoramaStateless.parseUrl(prioritaryUrl);
}
boursoramaStateless.parseUrl(url);
}
}
}
这里没问题。
我有一些异步调用(使用JMS)向我的_urls变量(List)添加一些值。目标是在我的无限循环中解析新的URL。
当我尝试通过JMS onMessage方法在我的List中添加新url时,我收到ConcurrentModificationException,但它似乎正在工作,因为这个新的url被解析了。
当我尝试包装同步块时:
while(true){
synchronized(_url){
// code...
}
}
我的新网址永远不会被解析,我希望在for()循环结束后解析...
所以我的问题是:如果在循环中访问List而没有ConcurrentModificationException,我怎么能修改List?
我只想要2个线程同时修改某些共享资源而不使用synchronized块...
答案 0 :(得分:1)
您可能需要CopyOnWriteArrayList
。
答案 1 :(得分:0)
首先,在运行Java EE容器时忘记synchronized
。它使容器无法优化线程利用率,并且无法在集群环境中工作。
其次,您的设计似乎是错误的。您不应该使用JMS更新bean的私有字段。这件事导致ConcurrentModificationException
。您可能应该修改bean以从数据库和MDB中检索集合以将URL存储到数据库中。
其他,更容易为您解决方案如下。 检索当前存在的URL并将其复制到其他集合。然后迭代这个集合。通过JMS更新全局集合时,更新在复制的集合中不可见,因此不会抛出任何异常:
while(true) {
for (String url : copyUrls(_prioritaryUrls)) {
// deal with url
}
}
private List<String> copyUrls(List<Stirng> urls) {
return new ArrayList<String>(urls); // this create copy of the source list
}
//........
public void onMessage(Message message) {
_prioritaryUrls.add(((TextMessage)message).getText());
}
答案 2 :(得分:0)
For (String s : urls)
在内部使用Iterator。迭代器检查并发修改,以便明确定义其行为。
您可以使用for(int i= ...
循环。这样,不会抛出任何异常,并且如果元素仅添加到List的末尾,您仍然可以获得一致的快照(在迭代期间的某个时间存在的列表)。如果列表中的元素被移动,则可能会丢失条目。
如果要使用synchronised
,则需要在两端进行同步,但这会导致并发读取丢失。
如果您想要并发访问和一致性快照,则可以使用java.util.concurrent
包中的任何集合。
已经提到过CopyOnWriteArrayList
。另一个有趣的是LinkedBlockingQueue
和ArrayBlockingQueue
(Collection
s但不是List
s),但那就是全部。
答案 3 :(得分:0)
所以我做了一些修改。
1)添加迭代器并保留synchronized块(内部parse()函数和addUrl()函数,它将新url添加到我的List中) - &GT;它像魅力一样工作,没有启动ConcurrentModificationException
2)添加了迭代器并删除了synchronized块 - &GT; ConcurrentModificationException仍然启动...
目前,我会详细了解您的答案并测试您的解决方案。
再次感谢你们