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