不修改对象却出现ConcurrentModificationException异常

4

我有下面这段引起了ConcurrentModificationException的代码。我没有对products对象进行修改,

List<Product> products = Product.findActiveByFilter(filters);
Set<Long> temp = new HashSet<Long>();
List<Product> resultSetTemp = new ArrayList<Product>();
for (Product product : products) { // << Exception points to this
    if(!temp.contains(product.getId())){
        temp.add(product.getId());
        resultSetTemp.add(product);
    }
}
products.clear();
products.addAll(resultSetTemp);

我已经多次见到这个异常弹出,但我无法复现它(它是随机发生的)。 Product.findActiveByFilter 是一个方法,它返回一个新的 List<Product> 实例,该实例是从缓存的 List<Product> 构建的。 编辑:我找到了一种复现错误的方法。当客户端想要产品(它是一个网店)时,代码被调用,并且当客户端向下滚动页面以加载更多项目时,会触发异常(因为服务器尚未完成响应产品并收到另一个调用)。竞争条件,有趣!

7
似乎有另一个线程试图修改它。 - serejja
1
异常发生在哪一行? - Boris the Spider
2
请问您能否发布 Product.findActiveByFilter 的代码? - BadIdeaException
为什么不使用 Map<Long, Product> 来存储你的结果,而不是保持两个集合同步? - Boris the Spider
1
好的。我问的原因是,你确定它返回的是缓存列表的副本而不是缓存列表本身吗?因为如果它返回的是缓存列表本身,并且你的代码在两个不同的地方同时运行(=并发),那么你将会得到你目前遇到的异常。 - BadIdeaException
显示剩余6条评论
2个回答

1

正如一些人已经说过的那样,它是由一个单独的线程“修改”products(因为它是一个缓存实例)引起的。 当没有应用筛选器时(因此没有返回筛选结果),我已将Product.findActiveByFilter的实现更改为返回一个new ArrayList<Product>(products);而不是对缓存值的引用。

public static List<Product> findActiveByFilter(ArrayList<FilterPair> filters) {
    List<Product> products = getCachedAllProductsByFirstSupplier();

    if (products == null) {
        products = new ArrayList<Product>();
    }

    if(filters.size() > 0) {
        List<Product> productsFiltered = new ArrayList<Product>(products);
        // ... MANY checks here for the filters ...

        return productsFiltered;
    }

    return new ArrayList<Product>(products); // Do not give cached copy, was 'return products;'
}

网站调用了两次findActiveByFilter方法。第一次调用包含了筛选条件,而第二次没有(因此第一次仍在忙碌中,而第二次直接返回)。


感谢您回复答案。很高兴它起作用了!看起来@serejja是正确的 :) - Keerthivasan

0
你尝试使用迭代器了吗?
List<Product> products = Product.findActiveByFilter(filters);
Set<Long> temp = new HashSet<Long>();
List<Product> resultSetTemp = new ArrayList<Product>();

Iterator ite = products.iterator();

while (ite.hasNext()) {

    Product product = ite.next();

    if(!temp.contains(product.getId())){

        temp.add(product.getId());
        resultSetTemp.add(product);
    }
}
products.clear();
products.addAll(resultSetTemp);

编辑:刚看到你的更新。考虑到这一点,这可能无法解决你的问题。


我认为这不会解决问题。当我们使用foreach循环时,会使用迭代器。 - Hunsu

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接