andrershov
andrershov

Reputation: 468

Java memory barriers

I'm reading JSR 133 Cookbook and have the following question about memory barriers. An example of inserted memory barriers is in the book, but only writing and reading from local variables is used. Suppose I have the following variables

int a;
volatile int b;

And the code

b=a;

Do I understand correctly that this one line would produce the following instructions

load a
LoadStore membar
store b

Upvotes: 3

Views: 883

Answers (1)

Kevin Sitze
Kevin Sitze

Reputation: 8280

The underlying behavior of the JVM is guaranteed only against the volatile variable. It may be possible that two separate threads may have access to different values for variable 'a' even after a thread completes evaluation of the b = a; statement. The JVM only guarantees that access to the volatile variable is serialized and has Happens-Before semantics. What this means is that the result of executing b = a; on two different threads (in the face of a "volatile" value for 'a' (ha ha)) is indeterminate because the JVM only says that the store to 'b' is serialized, it puts no guarantee on which thread has precedence.

More precisely what this means is that the JVM treats variable 'b' as having its own lock; allowing only one thread to read or write 'b' at a time; and this lock only protects access to 'b' and nothing else.

Now, this means different things under different JVMs and how this lock is actually implemented on different machine architectures may result in vastly different runtime behavior for your application. The only guarantee you should trust is what the Java reference manual says, "A field may be declared volatile, in which case the Java Memory Model ensures that all threads see a consistent value for the variable." For further review see Dennis Byrne's excellent article for some examples of how different JVM implementations deal with this issue.

Happens-Before semantics are not very interesting in the provided example because an integer primitive doesn't provide much opportunity for the kind of instruction reordering that volatile was intended (in part) to remedy. A better example is this:

private AnObjectWithAComplicatedConstructor _sampleA;
private volatile AnObjectWithAComplicatedConstructor _sampleB;

public void getSampleA() {
    if (_sampleA == null) {
        _sampleA = new AnObjectWithAComplicatedConstructor();
    }
    return _sampleA;
}

public void getSampleB() {
    if (_sampleB == null) {
        _sampleB = new AnObjectWithAComplicatedConstructor();
    }
    return _sampleB;
}

In this example field '_sampleA' has a serious problem; in a multithreaded situation it is very possible that '_sampleA' may be in the process of being initialized in one thread at the same time another thread attempts to use it leading to all sorts of sporatic and very, very difficult to duplicate bugs. To see this consider thread X to execute the 'new' byte code instruction statement of the new in getSampleA() and then stores the (yet-to-be-initialized) result in field '_sampleA'. Thread X is now paused by the JVM and thread Y starts executing getSampleA() and sees that the '_sampleA' is not null; which uninitialized value is then returned and thread Y now starts calling methods on the resulting instance causing all sorts of problems; which will, of course, only appear in production, at odd hours, and under heavy service loads.

The worse case for field _sampleB is that it may have multiple threads initializing individual instances; all but one of which will eventually be discarded. Code like this should be wrapped in a "synchronized" block but the volatile keyword will do the trick because it requires that the value finally stored in '_sampleB' has Happens-Before semantics which means that the stuff to the right of the equals sign is guaranteed to be complete when the stuff on the left hand side of the equals sign is performed.

Upvotes: 1

Related Questions