-1

I read that a ConcurrentModificationException can be thrown whenever a thread performs a structural modification upon a certain list while another thread is iterating over its elements. To detect such modification, instances of the class List store the number of times they were modified in a field called modCount, whose value is checked at each iteration of the list to check whether the latter was modified. If my understanding was correct, access to modCount needs to be synchronized, because if the list in question was to be modified after checking the value of modCount in the last iteration and before the end of the loop, the iterator would fail to detect that the list was modified during its last iteration.

Mehdi Charife
  • 722
  • 1
  • 7
  • 22
  • 1
    What do you think is easier to understand from our perspective? The code, or a paragraph of text describing the code? Show us, don't tell us. You haven't even told us what class you're talking about. I think it's probably ArrayList, but I shouldn't have to guess. – Michael Oct 18 '22 at 17:09
  • The source code for (e.g.) ArrayList is available; it contains likes like "modCount++" which is clearly not a use of an atomic variable, nor is it in a synchronized method. – access violation Oct 18 '22 at 17:13
  • @Michael Why do you assume that there is a code? My question is purely conceptual. It concerns the inner workings of the iteration process, and does not reflect a problem with some code that I have written. – Mehdi Charife Oct 18 '22 at 17:13
  • @ControlAltDel [it's not](https://stackoverflow.com/questions/25168062/why-is-i-not-atomic) – Michael Oct 18 '22 at 17:19
  • 2
    *"My question is purely conceptual"* No it isn't. It's about a specific implementation. That's as applied as it gets. "*does not reflect a problem with some code that I have written*" That doesn't mean you can't share the parts of the code you're talking about. – Michael Oct 18 '22 at 17:21
  • @accessviolation So a list modification could take place in the last iteration after checking the value of modCount without the iterator detecting it? – Mehdi Charife Oct 18 '22 at 17:22
  • 1
    @mehdicharife There is no guarantee that ConcurrentModificationException is thrown in all cases where concurrent modifications take place, no. To encounter that, one must be dealing with a non-thread safe implementation in an inherently dangerous way. In such case, the developers are basically doing you a favour by trying to detect it at all. They could not bother to detect it and open to door to whatever undefined behaviour occurs as a result. – Michael Oct 18 '22 at 17:25
  • @Michael "it's about a specific implementation" No it isn't. It's about all the implementations (instances) of the iteration process, and hence the conceptual predicate. "That doesn't mean that you can't share the parts of the code you're talking about". I'm not talking about any specific parts of code. Otherwise, I don't see the utility behind sharing a standard Java for loop. If you are unacquainted with the code of the latter, I'm not really sure you are qualified to answer my question. – Mehdi Charife Oct 18 '22 at 17:31
  • 1
    "*It's about all the implementations*" No. It's about several specific implementations. "*I'm not talking about any specific parts of code*" >>> "*a field called modCount*" – Michael Oct 18 '22 at 17:32
  • Re, "It's about all the implementations..." It's either about the library specification, or it's about the code. There is no `modCount` in the library specification. Also of note, the [Javadoc for the `List` interface](https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/util/List.html) says nothing whatsoever about what is supposed to happen when multiple threads access the same List. Docs for `ArrayList` and `LinkedList` only say that "If multiple threads access a linked list...it must be synchronized externally." There's no promise that an iterator will _reliably_ throw the... – Solomon Slow Oct 18 '22 at 20:51
  • ...exception if the list has been modified by a different thread than the one that is doing the iteration. – Solomon Slow Oct 18 '22 at 20:51
  • @Michael >>"It's about all the implementations" No.<< In the full quote I say: "It's about all the implementations (instances) of the iteration process", so please learn how to read or to quote correctly. Otherwise, I can't really help you clear out your misunderstandings. "It's about several specific implementations" You said before " It's about a specific implementation" so which is it? Being about several specific implementations is not necessarily in contradiction with being about all the implementations (instances) of the iteration process. On the contrary, the first entails the second. – Mehdi Charife Oct 18 '22 at 20:59
  • @Michael The second entails the first.* – Mehdi Charife Oct 18 '22 at 21:05
  • 2
    @SolomonSlow actually [`modCount` is documented](https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/util/AbstractList.html#modCount) in the library specification, though `ArrayList` and `LinkedList` do not say explicitly that they are using this field. – Holger Oct 20 '22 at 12:44

1 Answers1

5

Everything you've said is correct, and perfectly normal. modCount is not atomic, and the iterator could fail to detect that the list was modified, and that's fine. Revisit the ArrayList documentation:

Note that the fail-fast behavior of an iterator cannot be guaranteed as it is, generally speaking, impossible to make any hard guarantees in the presence of unsynchronized concurrent modification. Fail-fast iterators throw ConcurrentModificationException on a best-effort basis. Therefore, it would be wrong to write a program that depended on this exception for its correctness: the fail-fast behavior of iterators should be used only to detect bugs.

Because ArrayList does not try to guarantee that it will always detect a concurrent modification, it can avoid the overhead of an atomic variable for a case that should never happen in correct code. The use of the unsynchronized modCount detects the most common cases, and will sometimes (depending on exact thread ordering and the like) detect cases that are truly concurrent. Remember, though, that ConcurrentModificationException almost never happens because concurrency is actually going on, but because someone calls a modification method during an iteration loop.

Louis Wasserman
  • 191,574
  • 25
  • 345
  • 413
  • I probably wouldn't go as far as "almost never", but you're probably right that it's less common. – Michael Oct 18 '22 at 17:27
  • @user16320675 In a non-thread safe list? Why would it be? What do you mean "partially atomic"? That's an oxymoron – Michael Oct 18 '22 at 17:28
  • Atomicity costs more overhead in memory synchronization effort, so why should `ArrayList` bother -- again, when this should never actually happen in correct code? – Louis Wasserman Oct 18 '22 at 17:28
  • @Michael: I would confidently bet 99% of CMEs encountered by developers are from single-threaded code, which I'd call close enough to "almost never." – Louis Wasserman Oct 18 '22 at 17:29
  • @LouisWasserman Well, all I can say is that I'd take that bet... – Michael Oct 18 '22 at 17:30
  • @user16320675 I didn't think too hard about it, but my intuition is that whether you have an atomic write to an integer counter or not, that's probably not enough to guarantee throwing an exception in the case of concurrent read&writes. So whether atomic or not, the behaviour would be undefined either way. I think that's what you're getting at, right? – Michael Oct 18 '22 at 17:44
  • 1
    @user16320675 I suppose, the term “atomic” has been used here, because classes like `AtomicInteger` also imply memory visibility. `int` variables *are* atomic if we consider the real meaning of “atomic”. What it doesn’t have, is guarantees regarding memory visibility, but even if it had, e.g. by declaring the field `volatile`, it wouldn’t guaranty to prevent the application from inconsistencies in case of concurrent modifications. By the way, even a thread safe list doesn’t guaranty that, e.g. when iterating the `list` while `Collections.sort(list)` is done by another thread (before Java  8). – Holger Oct 20 '22 at 12:25