8

The docs say that you shouldn't lock on an instance of a value-based Java class such as Optional because code

may produce unpredictable results if it attempts to distinguish two references to equal values of a value-based class ... indirectly via an appeal to synchronization...

Why should Java's value-based classes not be serialized? asserts

Because future JVM implementations might not use object headers and reference pointers for value-based classes, some of the limitations are clear. (E.g. not locking on an identity which the JVM must not uphold. A reference on which is locked could be removed and replaced by another later, which makes releasing the lock pointless and will cause deadlocks).

I.E. that the prohibition is future-proofing. But there's no reference for that assertion.

If future-proofing is the basis, I'd like a reference for it. If not, I'd like to understand what the basis is since value-based objects are Objects.

EDIT

BTW, I understand the reasons not to lock on Integers and other primitive-wrapper classes; they may be cached. But I can find no documentation saying the same is true of value-based classes, and while Integer, &etc. are based on values, they are not value-based classes. I.E. The JavaDocs of Optional &etc. explicitly say

This is a value-based class

The same is not true for Integer, &etc.

Community
  • 1
  • 1
G. Ann - SonarSource Team
  • 22,346
  • 4
  • 40
  • 76
  • Probably this: http://openjdk.java.net/projects/valhalla/ (hopefully Java 10). – assylias Dec 02 '15 at 17:34
  • 3
    I am not sure it's just future proofing. If you get a reference to a Value-based class through a static factory method, that method may issue the same exact instance to some other thread when asked for the same value. E.g. `Integer.valueOf(5)` will give the same instance to two threads. So locking on that instance will cause it to be locked in the other thread unintentionally. – RealSkeptic Dec 02 '15 at 17:45
  • I'm aware of that restriction with the primitive wrapper classes, but `Integer` isn't a value-based class – G. Ann - SonarSource Team Dec 02 '15 at 19:01
  • @G.Ann-SonarSourceTeam Integer is a perfect example of a value based class. – assylias Dec 04 '15 at 09:13
  • @assylias please see my edit/expansion – G. Ann - SonarSource Team Dec 04 '15 at 12:24
  • When you say " I understand the reasons not to lock on Integers and other primitive-wrapper classes; they may be cached", that's not entirely right. It's a complication but not the main reason, see [Why is it not a good practice to synchronize on Boolean?](http://stackoverflow.com/q/10324272/217324): locks need to be constant object instances. – Nathan Hughes Dec 04 '15 at 18:23
  • @NathanHughes can you provide a reference saying that the reason given in http://stackoverflow.com/questions/10324272/why-is-it-not-a-good-practice-to-synchronize-on-boolean also applies to value-based objects? Because while it makes sense to me that that's the case, I can't find anything to confirm that. – G. Ann - SonarSource Team Dec 04 '15 at 18:53
  • You can cite my answer as a reference if you would like :-). Either way, it explains the potential hazard of locking on value-based classes; i.e. what could go wrong. – Stephen C Dec 06 '15 at 00:31

3 Answers3

8

Here's what a Blog post by Nicolai Parlog says about value-based classes:

In Java 8 value types are preceded by value-based classes. Their precise relation in the future is unclear but it could be similar to that of boxed and unboxed primitives (e.g. Integer and int). Additionally, the compiler will likely be free to silently switch between the two to improve performance. Exactly that switching back and forth, i.e. removing and later recreating a reference, also forbids identity-based mechanisms to be applied to value-based classes.

So what Nicolai is saying is this:

  • In the future, compilers may do things that transparently translate between values and value-based classes in ways that do not preserve object identity.

  • Certain things ("identity-based mechanisms") depend on object identity. Examples include the semantics of == for references, identity hashcode, primitive locking, and object serialization.

  • For those things, there is the potential that the transparent translation won't be transparent.

In the case of primitive locking, the concern is that something like the following sequence may occur.

  1. An instance of a value-based class is created.
  2. The instance is converted to a value behind the scenes.
  3. The value is then converted back, giving a different object.

If two threads then use "the instance" as a primitive lock, they could be unaware that in fact there are in fact two objects (now). If they then attempted to synchronize, they would (could) be locking different objects. That would mean there was no mutual exclusion on whatever the state was that the locking was intended to protect.

If you don't lock on a value-based class, you won't have to worry about that potential hazard ... in the future.

But note, that Nicolai's blog posting is one person's speculation on what might happen in Java 10 or later.


BTW, I understand the reasons not to lock on Integers and other primitive-wrapper classes; they may be cached.

Caching is not the problem per se, but a mechanism that gives rise to the problem. The real problem is that it is difficult to reason about the object identity of the lock object, and hence whether the locking regime is sound.

With the the primitive wrappers, it is the semantics of boxing and unboxing that gives rise uncertainty of object identity. Going forward, the mooted value type <-> object conversion would be another source of this uncertainty.


The above blog is based on "State of the Values" April 2014. John Rose, Brian Goetz, and Guy Steele which talks about adding value types to a future version of Java. This note is a position statement rather than a fully spec'd (and adopted) proposal. However the note does give us this hint:

"Many of the above restrictions correspond to the restrictions on so-called value-based classes. In fact, it seems likely that the boxed form of every value type will be a value-based class."

which could be read as implying that there will be a relationship between value types and existing value-based classes. (Especially if you read between the lines of the Java 8 description of value-based classes.)


UPDATE - 2019/05/18

Value types didn't make it into Java 12, and they are not (yet) on the list for Java 13.

However, it is already possible to demonstrate a problem that is related to the problem that the blog post talks about:

    public class BrokenSync {
        private final Integer lock = 1;

        public void someMethod() {
            synchronized (lock) {
                // do something
            }
        }
    }

The problem is that each instance of BrokenSync will create an Integer instance by auto-boxing 1. But the JLS says that Integer objects produced by auto-boxing are not necessarily distinct objects. So, you can end up with all instances of BrokenSync using the same Integer object as a lock.

Stephen C
  • 698,415
  • 94
  • 811
  • 1,216
  • what's the link for your documentation quote, please? – G. Ann - SonarSource Team Dec 09 '15 at 13:55
  • My mistake. The quote was Nicolai Parlog quoting himself. – Stephen C Dec 09 '15 at 14:39
  • Reading that post and its source blog were what made me ask for a documentation reference. I.E. I was hoping to see where the proposal had been accepted. However, I think the crux of the issue is "do not have accessible constructors, but are instead **instantiated through factory methods which make no committment as to the identity of returned instances;**", which means that you may be locking on the same instance another class is also trying to lock on => contention & deadlocks. At least, that's what I wrote in https://jira.sonarsource.com/browse/RSPEC-3436 – G. Ann - SonarSource Team Dec 09 '15 at 14:50
  • No. Value types have not been accepted for Java 9. (They have been proposed for Java 10 .... https://en.wikipedia.org/wiki/Project_Valhalla_%28Java_language%29 ) And what you wrote is definitely true ... through the Java 8 / 9 perspective. – Stephen C Dec 09 '15 at 14:57
  • Thanks. Vague-ified when it will break – G. Ann - SonarSource Team Dec 09 '15 at 15:16
0

A lock is associated with an object. If an object is shared, it's lock can be shared. Immutable value classes can be shared. In theory all references to a value object that has a particular semantic value could refer to one shared objects. It is common for creation code for value objects to reuse value objects. For example by caching previously created values. So in general when you have a reference to a value object your code should work correctly even if the value object is also used elsewhere. So don't use it for a lock.

Raedwald
  • 46,613
  • 43
  • 151
  • 237
0

All the needed information is right on the page you cite titled "Value-based Classes", although it's not written as clearly as it might be, and it doesn't use some magic phrases that would have clarified these issues.

The magic phrase I would use to describe this situation is that value-based classes can have implementation-defined behaviors. What the page says is that these classes "make no use of" reference equality ==. It doesn't say that the implementation may not define such an operator, but it does say, in essence, "we are remaining silent on this point". One of the phrases used is "make no commitment", which is a bit clearer.

For example, one kind of implementation might, say out of convenience, might make a value-based class V behave like most other objects and not bother suppressing the members that V makes no use of. Another might implement value-based classes differently, using the same internal mechanism as it uses for primitive-wrapper classes. (If you're building the VM, you don't have to implement every class in Java.) As long as the VM satisfies all the requirements (or if you like, contracts) as specified on this page, it's met its obligations to the user.

Now suppose you write code that locks such an object. The locking wasn't written to cope with this situation, so it will do something, but it need not be consistent from VM to VM.

To be specific as to your question, future-proofing is just a special case of implementation-defined behavior. How it's written today may not be how it's written tomorrow, even when both are legal.

eh9
  • 7,340
  • 20
  • 43