在没有ConcurrentModificationException的情况下处理List上的并发修改

时间:2012-09-24 12:31:08

标签: java list java.util.concurrent

我有一个有状态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块...

4 个答案:

答案 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。另一个有趣的是LinkedBlockingQueueArrayBlockingQueueCollection s但不是List s),但那就是全部。

答案 3 :(得分:0)

好的,谢谢你们。

所以我做了一些修改。

1)添加迭代器并保留synchronized块(内部parse()函数和addUrl()函数,它将新url添加到我的List中) - &GT;它像魅力一样工作,没有启动ConcurrentModificationException

2)添加了迭代器并删除了synchronized块 - &GT; ConcurrentModificationException仍然启动...

目前,我会详细了解您的答案并测试您的解决方案。

再次感谢你们