[concurrency-interest] Should I avoid compareAndSet with value-based classes?
gil at azul.com
Thu Jul 6 03:28:44 EDT 2017
Sent from my iPad
On Jul 5, 2017, at 11:51 PM, Henrik Johansson <dahankzter at gmail.com<mailto:dahankzter at gmail.com>> wrote:
Oh, without having followed the value type discussions I think it was a mistake to not "fix" equality. Why not make it a deep comparison if the reference is different? If it points to the same object we are done otherwise start checking the struct content.
There may be a lot I missed here but a new type of object could be allowed to have different meaning equality. Right?
.equals() means what you want it to mean. == and != (and the compare in compareAndSet) mean very specific things, and cannot be overridden.
For non-reference value types (int, long, char, etc.), == and != are value comparisons. An int has no identity. Just a value:
int a = 5;
int b = 5;
boolean y = (a == b); // true
For references to instances of (non value-based) classes, == and != can be thought of as comparing the value of the reference (and not the contents of the object instances). This is an identity comparison, which ignores values within the object:
Integer a = new Integer(5);
Integer b = new Integer(5);
boolean x = a.equals(b); // true
boolean y = (a == b); // false
And for references to value-based classes (which is a relatively new thing, but is part of Java 8), the meaning of == and != appears to be undefined. E.g.:
LocalDateTime a = LocalDateTime.parse("2007-12-03T10:15:30");
LocalDateTime b = LocalDateTime.parse("2007-12-03T10:15:30");
boolean x = a.equals(b); // true
boolean y = (a == b); // unpredictable, undefined, who knows.
// Could be true, could be false.
// Could theoretically change the values of a or b, or of something else
On Thu, 6 Jul 2017, 07:12 Gil Tene, <gil at azul.com<mailto:gil at azul.com>> wrote:
I'd take that documentation seriously. It basically says that ==, !=, synchronization, identity hashing, and serialization are undefined behaviors.
While the *current* implementations may carry some semi-intuitive behvaiors, e.g. where == indicates true when comparing two references to instances of a value-based class where the value of the references is the same, there is no guarantee that at some point in the [near or far] future that behavior will remain. Specifically, attempting == (or !=, or synchronization, etc., including compareAndSet) on a reference to a value based class is allowed to do ANYTHING in the future.
- It may throw an exception (something it should probably start doing ASAP to avoid future surprises).
- It may return always-false, even when the two references are "to the same instance" (and probably will, through many possible value-based compiler optimizations that will erase the unneeded notion of reference and identity).
- It may overwrite random locations in memory or to variables that the code performing the operation has the privilege to write to (which it probably shouldn't, but that's certainly included in what "undefined" and "unpredictable effects" can mean).
- It may sometimes do one of the above, and sometimes seem to be doing what you mean it to do. Switching between modes on a whim (e.g. when a tier 2 optimizing compilation is applied, or when the mutton is nice and lean and the tomato is ripe).
So no, there is no way for compareAndSet to work "correctly" on a reference to an instance of a value-based class. Even if it happens to appear to work "correctly" now, expect it to blow up in bad and potentially silent ways in the future.
> On Jul 5, 2017, at 9:47 PM, Brian S O'Neill <bronee at gmail.com<mailto:bronee at gmail.com>> wrote:
> I think the wording in the value-based document is too strong. It's perfectly fine to compare value based instances using ==, but it can lead to confusing results when comparing distinct instances with equivalent state. Using compareAndSet with a box isn't necessary for it to work "correctly" with a value-based class.
> By "correctly", I mean the compareAndSet operation works correctly, using == comparison. However, if your intention is for compareAndSet to compare Instants based on their state, then this of course won't work properly.
> If you want to perform a compareAndSet for an Instant's state (time since epoch), then you need to use something that can be compared atomically. This means the state must be representable in a 64-bit value or smaller. The Instant class measures time using a 64-bit long and a 32-bit int, and so this state cannot be compared atomically. You'd have to chop off some precision or use something else.
> On 2017-07-05 09:20 PM, Gil Tene wrote:
>> Reference equality for value based classes (as referenced below) lacks meaning, as there is no notion of identity in such classes (only a notion of value). And since compareAndSet on reference fields is basically an idenitity-based operation [in the compare part], the two won't mix well logically.
>> Specifically, while two references to e.g. java.time.LocalDateTime instances being == to each other *probably* means that the two are actually equal in value, the opposite is not true: Being != to each other does NOT mean that they are logically different. As such, the "compare" part in compareAndSet may falsely fail even when the two instances are logically equal to each other, leaving the rest of your logic potentially exposed.
>> Bottom line: given the explicit warning to not use == and != on references to value-based instances, I'd avoid using compareAndSet on those references. If you really need to use a value-based class in your logic, consider boxing it in another object that has [normal] identity.
>> — Gil.
>>> On Jul 5, 2017, at 8:59 PM, Michael Hixson <michael.hixson at gmail.com<mailto:michael.hixson at gmail.com>> wrote:
>>> AtomicReference and VarHandle are specified to use == in compareAndSet
>>> (and related) operations . Using == to compare instances of
>>> value-based classes may lead to "unpredictable results" . Does
>>> this mean I should avoid using compareAndSet with arguments that are
>>> instances of value-based classes?
>>> It seems like the documentation clearly tells me "yes, avoid doing
>>> that" but I'm hoping I misunderstood, or maybe AtomicReference and
>>> VarHandle are exempt somehow. Otherwise, how do I implement
>>> non-broken compareAndSet and updateAndGet for a java.time.Instant
>>> value for example? Do I have to box the value in something that's not
>>> a value-based class first, like AtomicReference<Box<Instant>>?
>>>  http://download.java.net/java/jdk9/docs/api/java/util/concurrent/atomic/AtomicReference.html#compareAndSet-V-V-
>>>  http://download.java.net/java/jdk9/docs/api/java/lang/doc-files/ValueBased.html
> Concurrency-interest mailing list
> Concurrency-interest at cs.oswego.edu<mailto:Concurrency-interest at cs.oswego.edu>
Concurrency-interest mailing list
Concurrency-interest at cs.oswego.edu<mailto:Concurrency-interest at cs.oswego.edu>
-------------- next part --------------
An HTML attachment was scrubbed...
More information about the Concurrency-interest