I have a method similar to the one below:
public void addSubjectsToCategory() {
final List<Subject> subjectsList = new ArrayList<>(getSubjectList());
for (final Iterator<Subject> subjectIterator =
subjectsList.iterator(); subjectIterator.hasNext();) {
addToCategory(subjectIterator.next().getId());
}
}
When this runs concurrently for the same user (another instance), sometimes it throws NoSuchElementException
. As per my understanding, sometimes subjectIterator.next()
get executed when there are no elements in the list. This occurs when being accessed only. Will method synchronization solve this issue?
The stack trace is:
java.util.NoSuchElementException: null
at java.util.ArrayList$Itr.next(Unknown Source)
at org.cmos.student.subject.category.CategoryManager.addSubjectsToCategory(CategoryManager.java:221)
This stack trace fails at the addToCategory(subjectIterator.next().getId());
line.
The ConcurrentModificationException occurs when an object is tried to be modified concurrently when it is not permissible. This exception usually comes when one is working with Java Collection classes. For Example - It is not permissible for a thread to modify a Collection when some other thread is iterating over it.
If an element is requested using the accessor methods of these classes or interfaces, and the underlying data structure does not contain the element, the NoSuchElementException is thrown. This can occur if the data structure is empty or if its next element is requested after reaching the end of the structure.
To Avoid ConcurrentModificationException in single-threaded environment. You can use the iterator remove() function to remove the object from underlying collection object. But in this case, you can remove the same object and not any other object from the list.
It is be cause NoSuchElementException is unchecked exception, which means that it "is-a" RuntimeException which does not force you to catch. The unchecked exceptions classes are the class RuntimeException and its subclasses, and the class Error and its subclasses.
The basic rule of iterators is that underlying collection must not be modified while the iterator is being used.
If you have a single thread, there seems to be nothing wrong with this code as long as getSubjectsList()
does not return null OR addToCategory()
or getId()
have some strange side-effects that would modify the subjectsList
. Note, however, that you could rewrite the for-loop somewhat nicer (for(Subject subject: subjectsList) ...
).
Judging by your code, my best guess is that you have another thread which is modifying subjectsList
somewhere else. If this is the case, using a SynchronizedList will probably not solve your problem. As far as I know, synchronization only applies to List methods such as add()
, remove()
etc., and does not lock a collection during iteration.
In this case, adding synchronized
to the method will not help either, because the other thread is doing its nasty stuff elsewhere. If these assumptions are true, your easiest and safest way is to make a separate synchronization object (i.e. Object lock = new Object()
) and then put synchronized (lock) { ... }
around this for loop as well as any other place in your program that modifies the collection. This will prevent the other thread from doing any modifications while this thread is iterating, and vice versa.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With