[concurrency-interest] Should I avoid compareAndSet with value-based classes?

Gil Tene gil at azul.com
Fri Jul 7 12:32:00 EDT 2017

> On Jul 7, 2017, at 1:39 AM, Andrew Haley <aph at redhat.com> wrote:
> On 06/07/17 17:33, Gil Tene wrote:
>> We could further discuss whether or not the JVM is allowed to
>> "falsely" indicate that == is true (or that != is false) even when
>> the instances differ in value (so are not .equals()). It is.
> No it isn't, unless you believe that the paragraph on value-based
> classes overrides the JLS.  For it to do so, the definition of
> reference equality would have to change.  Right now, all it says is
> "At run time, the result of == is true if the operand values are both
> null or both refer to the same object or array; otherwise, the result
> is false."  I suppose you could argue that in the context of a
> value-based class the notion of "same object" is vacuous, but that
> would be a heck of a stretch.
>> Doing that may certainly surprise anyone who uses the identity based
>> == for anything to do with instances of value-based classes, even
>> tho using it would be silly given that == might obviously be
>> always-false. For example, I could see how someone may mistakenly
>> try to use == as an "optimization" on the assumption that if they
>> get lucky and == is true, .equals() must be true as well, and that
>> evaluating == might be cheaper, but they would still "do the right
>> thing" in the != case. But that would be a mistake, and a clear
>> violation of the "don't do that" admonition above.
> Frankly, I don't believe it.  Any JVM which did what you suggest would
> IMO be in violation of the JLS and the JVM specification.  It is
> possible that the definition of reference equality will be changed, but
> that would require the incompatible specification change to be
> discussed.

The argument would be (as you note above in the "heck of a stretch") that the field we use to talk about the instance is not a reference, but something else. Because a reference is an object identity, and value-based classes have no object identity. So it is something else. A "value identifier" of some sort. And the == operator is simply undefined on that something else. It certainly doesn't have the meaning of "IFF == is true then the value is the same" (which may be want we'd want it to mean). And it can't possibly mean "if == is the same the the object identity is the same" because there is no identity…

The "ugly" part of all this is that the compiler (and the way this stuff is derived from Object) will let you write this stuff about identity that doesn't exist, so we get to argue over what it means, and what reasonable or unreasonable ways to deal with undefined operations on non-existent identity would be.

When discussing how far a "reasonable" JVM would go:

1.  I'd agree with you that I would feel too nervous about making this undefined-behavior == operation evaluate as true for non-equal values.
 [this shouldn't make people writing Java code "comfortable" with interpreting the operand to mean that the values must be equal. It is still undefined. And this is still wrong code to write. But I'm going to want to avoid arguing too much about it, so...]

2. I would feel completely comfortable with == evaluating as always false, and != evaluating as always-true. 
[Those are clearly possible and valid results. The "reference" *could* be taken away at any point, and freely replaced with something else for which .equals() is true. So acting like that happens every single time is completely reasonable. This is what makes CAS useless BTW, since it may always fail even with no contention.]

3. The exception-throwing thing is IMO the healthy way to go IMO. I.e. throw some runtime exception when encountering an indentity-based operation on an instance of a value-based class. Hard to argue that "an exception should not be thrown here" given the clear warnings in the spec. My worry is mostly about coverage. Throwing exceptions in all cases where identity based operations are attempted would clearly be right, and healthy. But doing so only in some cases (and leaving some undefined behavior executing paths that don't throw exceptions) doesn't solve the problem [and may make things worse by encouraging false confidence]. Since no one *requires* us to throw an exception right now, JVM implementors will probably keep taking the lazy approach and not work hard on trying to achieve complete coverage. And since don't do it at all" is probably better/easier to verify than "do it in some places but not in others", we'll probably avoid it altogether for now. This *may* change when value-based optimizations materialize.

> -- 
> Andrew Haley
> Java Platform Lead Engineer
> Red Hat UK Ltd. <https://www.redhat.com>
> EAC8 43EB D3EF DB98 CC77 2FAD A5CD 6035 332F A671

More information about the Concurrency-interest mailing list