On 5/15/18 8:02 PM, Martin Buchholz wrote:
How many times the lock is acquired is an implementation detail, a non-obvious
tradeoff even.
vector.replaceAll holds the lock throughout, but vector.subList(0,
size).replaceAll holds the lock for each element (sigh ... racily (really a bug!)).
In Vector's case it's specified, not an implementation detail. You can't perform
certain bulk operations on a Vector without holding the lock externally.
Again, imagine this use case: there is a periodic background task that
optimizes all the elements of a Vector
vector.replaceAll(x -> optimized(x))
That should not break any iterations in progress.
I don't think it's possible to do that correctly without holding a lock
around the entire iteration.
I don't see why.
If the lock is held, CME can't occur, as a concurrent replaceAll() will
occur before or after the iteration, never in the middle.
I don't see why - iteration is inherently non-atomic, so replaceAll could be
called in the middle.
If an iteration over a Vector doesn't hold a lock, any read-modify-write
operations (consider a loop with a ListIterator on which set() is called)
can be interleaved with bulk operations (like replaceAll) which is clearly
incorrect. In such cases, CME should be thrown.
Imagine your iterators are all read-only, and don't care whether they see an
element or its replacement.
You're imagining an incredibly narrow use case. You're choosing Vector, not
ArrayList; the replaceAll() operation must an optimization that doesn't affect
the semantics of the object (say, the outcome of any logic); the iteration must
be read-only, not read-modify-write; and the logic within the iteration "doesn't
care" whether it gets old or new values.
I don't find it compelling that it's possible to imagine such a case. Most code
won't conform to it. And in fact it's really hard to tell whether it does.
Consider an example like this:
for (T t : vectorOfT) {
print(t);
}
Suppose that a replaceAll() on another thread occurs, and that this is allowed.
Does the application care whether the eventual printout contains partly new
values and partly old values? How can you tell? It seems to me that this is more
likely a programming error than a valid use case.
Also, this use case cannot be written today, because CME is thrown.
?? Imagine there's only one writer thread, with some Iterating reader threads.
Every midnight, the writer thread optimizes all the elements
for (int i = 0; i < size; i++) vector.set(i, optimized(vector.get(i))
This code has worked well since the '90s without CME. One day the maintainer
notices shiny new Vector.replaceAll and "fixes" the code.
"""It's perfectly safe""".
The CME is rare and so only caught when the maintainer has gone on to the next
job. The CMEs only happen at midnight!
By "cannot be written today" I mean that the following:
for (T t: arrayList) {
if (condition) {
list.replaceAll();
}
}
has ALWAYS thrown CME, since the introduction of replaceAll().
Sure, there are cases such as you describe where CME gets thrown only rarely.
That's preferable to getting incorrect results equally rarely. That's the point
of fail-fast.
**
(subsequent email)
(We don't seem to be moving towards consensus ...)
Apparently....
At the very least, having replaceAll increment modCount (inconsistently!) is
surprising to users and is not documented anywhere.
Maybe it should be documented then.
**
Why are you placing so much emphasis on *removing* the CME behavior? It's been
this way since Java 8 was delivered. Is this causing a problem? What will be
solved by removing it?
s'marks