G. Ann - SonarSource Team
G. Ann - SonarSource Team

Reputation: 22804

Why not lock on a value-based class

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.

Upvotes: 10

Views: 3512

Answers (3)

Stephen C
Stephen C

Reputation: 719229

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.

Upvotes: 8

eh9
eh9

Reputation: 7430

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.

Upvotes: 0

Raedwald
Raedwald

Reputation: 48682

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.

Upvotes: 0

Related Questions