Pregunta

I've got the following piece of code that is causing a ConcurrentModificationException. I'm not modifying the products object at all,

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);

I've seen this exception pop-up several times, but I cannot reproduce it (it happens randomly).

Product.findActiveByFilter is a method that returns a new instance of a List<Product> that has been build from a cached List<Product>.

Edit: I've found a way to reproduce the error. The code is called when a client wants the products (its a webshop), and the website loads more items when the client scrolls down. This triggers the exception (as the server is not-yet done responding with the products, and gets another call for it). Race conditions, fun!

¿Fue útil?

Solución

As some said already, it was caused by a seperate tread 'modifying' products (as it was a cached instance). I've changed the implementation of Product.findActiveByFilter to return a new ArrayList<Product>(products); instead of a reference to the cached value, when no filters are applied (thus no filtered result is given back).

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;'
}

There were 2 calls to the findActiveByFilter, called by the website. The first one did include a filter, but the second one did not (so the first one was still busy, while the second one returned directly).

Otros consejos

Have you tried using an iterator?

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);

EDIT: Just saw your update. In light of that, this probably won't solve your problem.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top