[concurrency-interest] AtomicReference.updateAndGet() mandatory updating

David Holmes davidcholmes at aapt.net.au
Tue May 23 20:12:57 EDT 2017


Hi Mike,

What you suggest is too racy. As soon as you call get() the value may change such that you do want to update it - even if back to what it was previously. Only by doing the CAS can you be sure that nothing changed and you truly updated things in the manner expected.

Cheers,
David

> -----Original Message-----
> From: Concurrency-interest [mailto:concurrency-interest-bounces at cs.oswego.edu] On Behalf Of Mike Duigou
> Sent: Wednesday, May 24, 2017 9:59 AM
> To: Concurrency Interest <concurrency-interest at cs.oswego.edu>
> Subject: [concurrency-interest] AtomicReference.updateAndGet() mandatory updating
> 
> Currently the AtomicReference updateAndGet implementation will unconditionally perform a compareAndSet using the result of the
> update function.
> 
> public final V updateAndGet(UnaryOperator<V> updateFunction) {
>    V prev, next;
>    do {
>      prev = get();
>      next = updateFunction.apply(prev);
>    } while (!compareAndSet(prev, next));
>    return next;
> }
> 
> I find that I write a lot of update functions which only occasionally change the value. For these cases I wonder if it would be reasonable
> to skip the update if the value of next is unchanged from previous. A proposed alternative (there are analogues for Integer and Long).
> 
> public final V updateAndGet(UnaryOperator<V> updateFunction) {
>    V prev, next;
>    do {
>      prev = get();
>      next = updateFunction.apply(prev);
>    } while (prev != next && !compareAndSet(prev, next));
>    return next;
> }
> 
> The cases to consider are:
> 
> 1. prev == value == next :: Nothing changed.
> 
> In this case omitting the compareAndSet is a useful optimization. No difference for external observer.
> 
> 
> 2. (prev == value) != next :: Only next changed
> 
> In this case the compareAndSet would be performed and succeed as prev ==
> value. No difference for external observer.
> 
> 
> 3. prev != value != next :: Both next and value changed. A concurrent
> modification.
> 
> In this case the compareAndSet would be performed and will fail as prev
> != val. No difference for external observer.
> 
> 
> 4. (prev == next) != value :: Only value changed. A concurrent
> modification.
> 
> In the original implementation the compareAndSet would fail resulting in
> another update attempt. In proposed implementation the compareAndSet
> would not be attempted and the value returned would be unchanged. The
> concurrent modification of value is ignored. Surely this is wrong! It
> appears wrong because some other thread beat us and updated the value.
> The question is whether our thread could tell the difference between the
> call it made to updateAndGet completing first and a concurrent update
> being ignored. Other than via side-effects in updateFunction it does not
> appear that it could. To an external observer this case is
> indistinguishable from the first where there was no concurrent update.
> Even if there is a concurrent update it will appear to callers that
> non-mutating updates always completed first.
> 
> Useful? Wrongheaded?
> 
> Mike
> _______________________________________________
> Concurrency-interest mailing list
> Concurrency-interest at cs.oswego.edu
> http://cs.oswego.edu/mailman/listinfo/concurrency-interest



More information about the Concurrency-interest mailing list