29

As far as I know reference assignment is atomic in a 64 bit JVM. Now, I assume the jvm doesn't use atomic pointers internally to model this, since otherwise there would be no need for Atomic References. So my questions are:

Is atomic reference assignment in the "specs" of java/Scala and guaranteed to happen or is it just a happy coincidence that it is that way most times ?

Is atomic reference assignment implied for any language that compiles to the JVM's bytecode (e.g. clojure, Groovy, JRuby, JPython...etc) ?

How can reference assignment be atomic without using an atomic pointer internally ?

3
  • @JornVernee I meant assignment not swapping, thanks for the correction Commented May 31, 2017 at 11:21
  • This might give a first clue: stackoverflow.com/questions/23232242/… Commented May 31, 2017 at 11:35
  • I did... however I've been searching all over the internet for how getfield, putfiled and iastore are implemented and I haven't found an explaination that doesn't involve digging through the openjvm code, which I'm not proficient enough to do :( Commented May 31, 2017 at 12:23

3 Answers 3

36

First of all, reference assignment is atomic because the specification says so. Besides that, there is no obstacle for JVM implementors to fulfill this constraint, as 64 Bit references are usually only used on 64 Bit architectures, where atomic 64 Bit assignment comes for free.

Your main confusion stems from the assumption that the additional “Atomic References” feature means exactly that, due to its name. But the AtomicReference class offers far more, as it encapsulates a volatile reference, which has stronger memory visibility guarantees in a multi-threaded execution.

Having an atomic reference update does not necessarily imply that a thread reading the reference will also see consistent values regarding the fields of the object reachable through that reference. All it guarantees is that you will read either the null reference or a valid reference to an existing object that actually was stored by some thread. If you want more guarantees, you need constructs like synchronization, volatile references, or an AtomicReference.

AtomicReference also offers atomic update operations like compareAndSet or getAndSet. These are not possible with ordinary reference variables using built-in language constructs (but only with special classes like AtomicReferenceFieldUpdater or VarHandle).

Sign up to request clarification or add additional context in comments.

21 Comments

An “atomic pointer” isn’t necessarily an “atomic reference”, in the sense of AtomicReference. Hence, it was necessary to point out that references (i.e. the underlying pointers) are atomic, still don’t match the functionality of an AtomicReference.
“Publishing” means assigning to a variable that is visible to other threads. And no, there are no guaranties regarding visibility of previous updates when publishing though normal references. If you write point=new Point(42,100) and point is neither final nor volatile, other threads may see the new reference, while still seeing the default value (0) for either, x or y , or both (in the absence of other synchronization).
This is not about an older instance; the reference might have been null before the assignment, that doesn’t matter. The issue is, new Point(42,100) is not an atomic operation. It creates a new Point instance, all fields being at their default value (0 for int), then, the constructor will be executed, which will assign the values 42 to x and 100 to y, then the reference is assigned to point, from a single thread’s point of view. A different thread may see these actions out of order, see the reference to the new Point instance without seeing the effect of the assignments.
This doesn’t apply to immutable objects. If you declare x and y as final, they’re immune to such data races. Alternatively, you may declare point as final, but this implies that the whole action is part of a constructor or initializer. Or, well, declaring point as volatile ensures that other threads see all previous updates (x=42; y=100;) when seeing the new Point instance through the point reference.
@Powet any reference assignment that doesn’t care about the previous reference, is atomic. The update operations provided by AtomicReference include compareAndSet (“only assign the new reference if the old reference has the expected value”) and getAndSet (“give me the old reference after assigning the new one”). If you try to implement the same logic without AtomicReference, it will inevitably consist of more than one access to the field, so it’ll be obvious why this wouldn’t be atomic (without additional measures).
|
10

Atomic reference assignment is in the specs.

Writes to and reads of references are always atomic, regardless of whether they are implemented as 32 or 64 bit values.

Quoted from JSR-133: Java(TM) Memory Model and Thread Specification, section 12 Non-atomic Treatment of double and long, http://www.cs.umd.edu/~pugh/java/memoryModel/jsr133.pdf.

6 Comments

Ok, that answers the first part but not the other 2. To be honest the thing that I'm most curios about here is how this behavior happens if references are not atomic pointers and that's related to how one would implement a JVM rather than the specifications for java and/or the jvm.
True. I on purpose did not pretend to be a wiseguy on matters where I’m not sure. The way I understand it, though, this is a spec of the JVM, so I would expect it to cover all languages running on the JVM. And the way I read it, in requires internal pointer operations to happen atomically (at least as seen from outside the JVM).
Well yes, but I doubt those are actual atomic pointer operations since otherwise things like compare and swap would also be atomic using standard references.
Nice answer - good find; my vote for that. And thanks for leaving out that small part that left room for my answer ;-)
The linked PDF describes the memory model for Java 5 proposed by JSR133. To prove that it indeed made it into the Java 5 JLS and is still there today, it’s better to link to JLS §17.7.
|
4

As the other answer outlines, the Java Memory Model states that references read/writes are atomic.

But of course, that is the Java language memory model. On the other hand: no matter if we talk Java or Scala or Kotlin or ... in the end everything gets compiled into bytecode.

There are no special bytecode instructions for Java. Scala in the end uses the very same instructions.

Leading to: the properties of that memory model must be implemented inside the VM platform. Thus they must apply to other languages running on the platform as well.

7 Comments

Since the name was ' JavaTM Memory Model' I was unsure if it referred to the JVM as a whole and as such I assumed there might be instructions to non atomically asign available to other JVM languages
Back then when that memory model was written, there was only the Java language on the Java VM platform. Methinks.
Even now, I don't think there are JVM instructions that aren't used by Java itself.
@Jasper-M: there are. E.g., swap is never used by javac generated code. In Java 7, the invokedynamic instruction was not used by Java itself, which is quite a big feature of that JVM version…
One thing to keep in mind when drawing this conclusion, is, that this only applies if the concepts of that other language (i.e. references) are compiled 1:1 to the equivalent byte code artifacts. E.g. a reference of a particular language might be so far away from what Java’s object references do, that it doesn’t match a sole byte code reference variable, but, e.g. a compound data structure. For Java, it is impossible to encounter an object reference having a not-yet-initialized vtable, but that doesn’t have to apply to another language, having an entirely different method dispatch algorithm.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.