Alex
Alex

Reputation: 1749

How to ignore ConcurrentModificationException

I have a Java 11, JavaFX, Spring JPA / boot, MySQL application. When this application starts, it pulls larger amounts of data to calculate several Key Performance Indicators [KPI] like in a dashboard. One day I might move this calculations to the database - but for the time being it is with the application. Each of these KPIs is calculated in a dedicated thread. Once the KPI is available, it is shown in the UI in an area of the UI. The user may and will likely start to use other functionality of the application. It is very likely, that this functionality operates on the same objects as the KPI calculation. Hence, when that happens, I always get a ConcurrentModificationException. However, it happened when the other functionality would load data - it is not the user modifying or saving data. Hence I was surprised. I added a breakpoint to the debugger, such that whenever something is accessing the ArrayList with the related Objects it should stop. That never happened, other then my KPI calculation was accessing it. So when trying to understand this, it turns out, that because of optimistic locking the version number of the object domains gets changed by Spring. So, actually I don't care about the changed version number while calculating the KPIs. However, the trouble is that the KPI calculation loop throws the Exception and hence I miss this according object in my KPI calculation. Is there a way to "ignore" the Exception? In other words, how would I make sure that in below example still all tasks efforts are counted?

...  // previous code

try {
    ArrayList taskList = taskService.getAllTasks();
    for(Task task: taskList) {                     // This line throws the Exception
       totalEffortHours += task.getEffort();
    }
} catch(ConcurrentModificationException cme) {
}

...  // following code

Thank you in advance.

Upvotes: 0

Views: 203

Answers (1)

Stephen C
Stephen C

Reputation: 718758

Is there a way to "ignore" the Exception? In other words, how would I make sure that in below example still all tasks efforts are counted?

No there isn't.

  • If you ignore the exception then you won't count them all.
  • Indeed ... if the exception is thrown then you won't count them all!

You need to figure out why the exception is being thrown and change the code so that that doesn't happen.

Based on the code snippets you have provided, there are two possible causes for the concurrent modification that leads to the exception:

  1. Some other thread is (sometimes) updating the list returned by the taskService.getAllTasks() call while you are counting.

  2. The task.getEffort() call somehow changes the list.

The second cause is theoretical only. (I can't imagine someone writing code that did that!) Which leaves us with just one plausible explanation. Another thread is modifying the list.

(A problem like this is likely to be timing sensitive. I am not at all surprised that it only occurs occasionally, and that it doesn't happen when you attempt to debug the code.)

How to fix this (assumed) concurrent update problem?

One approach is to modify the code so that the task service's task list is a concurrent collection type; i.e. one that doesn't throw CCMEs. Possibilities include CopyOnWriteArrayList, ConcurrentDeque or one of the concurrent set or map classes.

The other approach is to use mutual exclusion to prevent any updates to the task list while you are iterating the list to accumulate the effort values.

(Note that just copying a non-concurrent list isn't a solution ... because the CCME will just happen while you are making the copy!)

Upvotes: 1

Related Questions