So, you've got one thread (the "background job") that periodically constructs a new object, and then stores a reference to it in a global variable.
The problem is, it's not just the "staleness" of the global variable that you have to worry about. Each individual field of the shared object can be "stale" independently of each other field. Each part of each sub-object can be individually "stale" independent of any other part.
If the shared object is anything more complicated than just a single shared Integer
, then the "background job" has to construct it piece-by-piece. And, with no synchronization between the threads, any other thread that looks at that object could see some parts of it "constructed" while other parts appear to be not-yet-constructed.
Without synchronization, the client threads are not guaranteed to see fields of the object updated in the same order in which the background job performed the assignments. An earlier assignment might come through, while a later assignment still appears to be "stale."
Suppose you have this:
class SharedObj {
public int x;
public int y;
public static SharedObj globalRef = new SharedObj();
}
And suppose the background thread does this:
SharedObj localRef = new SharedObj();
localRef.x = 3;
localRef.y = 5;
SharedObj.globalRef = localRef;
Finally, in some other thread:
SharedObj localRef = SharedObj.globalRef;
if (localRef.y < localRef.x) {
System.out.println("Boo!!");
}
Can the other thread print "Boo!!"? Yes. It can. I don't know all of the possibilities, but one possibility is, it could see x==3
and y==0
.
You can use volatile
to guarantee that the program will never print "Boo!!":
static volatile SharedObj globalRef = new SharedObj();
It works because volatile
writes and reads establish a "happens before" relationship between the two threads. The volatile
keyword guarantees that everything the "background job" did before it updated the globalRef
must become visible to any other thread when the other thread reads the new reference from the globalRef
.