1

我有一个有状态的 EJB,它调用 Web 解析页面的 EJB 无状态方法。

这是我的有状态代码:

@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 变量(列表)中一些值。目标是在我的无限循环中解析新的 url。

当我尝试通过 JMS onMessage 方法在我的列表中添加新 url 时收到 ConcurrentModificationException 但它似乎正在工作,因为这个新 url 已被解析。

当我尝试包装同步块时:

while(true){
    synchronized(_url){
        // code...
    }
}

我的新网址永远不会被解析,我希望在 for() 循环完成后被解析......

所以我的问题是:在没有 ConcurrentModificationException 的情况下,如何在循环内访问 List 时修改它?

我只想让 2 个线程在没有同步块的情况下同时修改一些共享资源......

4

4 回答 4

1

你可能想要一个CopyOnWriteArrayList.

于 2012-09-24T12:37:24.107 回答
0

首先,忘记synchronized运行 Java EE 容器的时间。它困扰容器以优化线程利用率,并且在集群环境中不起作用。

其次,您的设计似乎是错误的。您不应该使用 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());
}
于 2012-09-24T12:43:07.737 回答
0

For (String s : urls)在内部使用迭代器。迭代器检查并发修改,以便明确定义其行为。

您可以使用for(int i= ...循环。这样,不会引发异常,并且如果仅将元素添加到列表的末尾,您仍然可以获得一致的快照(列表在迭代期间的某个时间存在)。如果列表中的元素被移动,您可能会丢失条目。

如果要使用synchronised,则需要在两端同步,但那样会丢失并发读取。

如果您想要并发访问和一致的快照,您可以使用java.util.concurrent包中的任何集合。 CopyOnWriteArrayList已经提到过。其他有趣的是LinkedBlockingQueueand ArrayBlockingQueue( Collections but not Lists) 但仅此而已。

于 2012-09-24T12:54:20.227 回答
0

好的,谢谢你们。

所以我做了一些修改。

1) 添加迭代器并离开同步块(在 parse() 函数内部和 addUrl() 函数周围,它将新 url 添加到我的列表)-> 它的工作就像一个魅力,没有 ConcurrentModificationException 启动

2) 添加迭代器并移除同步块 --> ConcurrentModificationException 仍在启动...

现在,我将阅读有关您的答案的更多信息并测试您的解决方案。

再次感谢你们

于 2012-09-24T13:14:54.647 回答