Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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.

like image 346
G. Ann - SonarSource Team Avatar asked Dec 02 '15 17:12

G. Ann - SonarSource Team


People also ask

What is a value based class?

Value-based classes, as defined in the Java docs, are classes which conform to a set of rules: - are final and immutable (though may contain references to mutable objects);

Which method releases the lock?

A lock is acquired via the lock() method and released via the unlock() method. Invoking an unlock() without lock() will throw an exception.

How is locking mechanism achieved in multithreading?

A thread gets blocked if it can't get an access to the synchronized block. The Lock API provides tryLock() method. The thread acquires lock only if it's available and not held by any other thread. This reduces blocking time of thread waiting for the lock.

Can a thread acquire multiple locks?

With locking, deadlock happens when threads acquire multiple locks at the same time, and two threads end up blocked while holding locks that they are each waiting for the other to release. The monitor pattern unfortunately makes this fairly easy to do. Here's an example.


1 Answers

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.

like image 144
Stephen C Avatar answered Oct 16 '22 09:10

Stephen C