Monday, 12 August 2013

What is the memory visibility of variables accessed in static singletons in Java?

What is the memory visibility of variables accessed in static singletons
in Java?

I've seen this type of code a lot in projects, where the application wants
a global data holder, so they use a static singleton that any thread can
access.
public class GlobalData {
// Data-related code. This could be anything; I've used a simple String.
//
private String someData;
public String getData() { return someData; }
public void setData(String data) { someData = data; }
// Singleton code
//
private static GlobalData INSTANCE;
private GlobalData() {}
public synchronized GlobalData getInstance() {
if (INSTANCE == null) INSTANCE = new GlobalData();
return INSTANCE;
}
}
I hope it's easy to see what's going on. One can call
GlobalData.getInstance().getData() at any time on any thread. It's
thread-safe so I'm not worried about that. (Before you jump on this, yes
it is thread-safe. If two threads call setData() with different values,
just because you can't guarantee which one "wins" doesn't mean it isn't
thread-safe)
But thread-safety isn't my concern here. What I'm worried about is memory
visibility. Whenever there's a memory barrier in Java, the cached memory
is synched between the corresponding threads. A memory barrier happens
when passing through synchronizations, accessing volatile variables, etc.
Imagine the following scenario happening in chronological order:
// Thread 1
GlobalData d = GlobalData.getInstance();
d.setData("one");
// Thread 2
GlobalData d = GlobalData.getInstance();
d.setData("two");
// Thread 1
String value = d.getData();
Isn't it possible that the last value of value in thread 1 can still be
"one"? The reason being, thread 2 never called any synchronized methods
after calling d.setData("two") so there was never a memory barrier? Note
that the memory-barrier in this case happens every time getInstance() is
called because it's synchronized.

No comments:

Post a Comment