I'd like some clarity on what exactly the happens-before property means.
I've seen explanations for the happens-before property say that updates to global variables (which are not volatile or enclosed in a sync block) are made visible to other threads if they are altered before some other variable which is volatile or altered within a sync block. Is this right? If so, where in the java documentation does it say this?
My understanding was that the happens-before property defines the relationship between shared fields and code execution e.g:
For example:
class Shared {
private int y = 0;
private volatile int x = 0;
public void setOne() {
y = 1;
x = 1;
}
public int getY() {
return y;
}
}
For the code above, given 2 threads:
Shared shared = new Shared();
new Thread(() -> shared.setOne()).start();
new Thread(() -> shared.getY()).start();
Edit Assuming we can guarantee that the first thread has started, would getY() return 0 or 1 here?
I've also seen examples saying that this behavior happens only following a read of a volatile field in a thread. So in that case if one thread reads the value of the volatile field (let's say thread B), then all fields written before that volatile field in thread A are available to thread B. According this, if I modify the getY() method in Shared object from above to be:
public int getXPlusY() {
int local = x;
return local + y;
}
Is it this action that makes y visible to the other thread?
Let's look at your second example first.
class Shared {
private int y = 0;
private volatile int x = 0;
public void setOne() {
y = 1; //(1)
x = 1; //(2)
}
public int getXPlusY() {
int local = x; //(3)
return local + y; //(4)
}
}
We know that there is a happens-before relationship between (1) and (2) due to program order:
If x and y are actions of the same thread and x comes before y in program order, then hb(x, y).
Since x
is volatile, we know that there's a happens-before relationship between (2) and (3)
A write to a volatile field (§8.3.1.4) happens-before every subsequent read of that field.
And there is a happens-before relationship between (3) and (4) due to program order again:
If x and y are actions of the same thread and x comes before y in program order, then hb(x, y).
Therefore, we have a happens-before chain (1) → (2), (2) → (3), (3) → (4)
And since happens-before is a transitive relation (if A happens before B and B happens before C, then A happens before C) that means that (1) has a happens-before relation with (4).
Now let's look at the first example:
class Shared {
private int y = 0;
private volatile int x = 0;
public void setOne() {
y = 1; //(1)
x = 1; //(2)
}
public int getY() {
return y; //(3)
}
}
There's again a happens-before relation between (1) and (2), but that's about it. Since x
is not read by the second thread, we have no happens-before between (2) and (3). Therefore we have no happens-before relation between (1) and (3).
The quotes were taken from Chapter 17 of the Java Language Specification (JLS)