static My worker;
static worker() {
if (worker == null) {
synchronized {
if (worker == null) {
worker = new MyImplementation();
}
}
}
return worker;
}
Now you feel good, the threads would not reinitialize the same singleton, because when the second threads penetrates the synchronized block, worker is already not null. You feel good... but then you open Josh and Neal's Java Puzzlers, and see that oops, this won't work on a multiprocessor machine. Why? See, by the time the first thread leaves the syncrhonized block, the value of worker may not reach the memory shared by the threads. >>
Nope. The only real reason could be that the Java compiler remembers the value of "worker" in the virtual machine registers/stack before it gets the mutex, and then when the mutex synchronizes the memory, the old value from the registers continues to be used throughout the constructor. That's why "volatile" fixes it: it requires that the value gets pulled from the memory afresh every time. I would expect it from C++, but then Java has synchronization as a language statement, so it should treat these statements as barriers for storing temporary values in registers.
BTW, probably a simpler initialization is something like this (my Java nay contain syntax errors):
class My;
// this class will provide synchronization for
// the initialization of class My
class MyHelper {
static synchronized void getMy()
{
My::initialize();
}
};
class My {
protected:
friend class MyHelper;
static My worker;
static void initialize()
{
if (worker == null)
worker = new My();
}
public:
static My worker()
{
MyHelper::getMy();
return worker;
}
};
Overall the Java synchronized stuff is way too uglier than it seems at first :-( And singletons are Evil.
no subject
Date: 2007-04-12 04:42 pm (UTC)Now you feel good, the threads would not reinitialize the same singleton, because when the second threads penetrates the synchronized block, worker is already not null. You feel good... but then you open Josh and Neal's Java Puzzlers, and see that oops, this won't work on a multiprocessor machine. Why? See, by the time the first thread leaves the syncrhonized block, the value of worker may not reach the memory shared by the threads.
>>
Nope. The only real reason could be that the Java compiler remembers the value of "worker" in the virtual machine registers/stack before it gets the mutex, and then when the mutex synchronizes the memory, the old value from the registers continues to be used throughout the constructor. That's why "volatile" fixes it: it requires that the value gets pulled from the memory afresh every time. I would expect it from C++, but then Java has synchronization as a language statement, so it should treat these statements as barriers for storing temporary values in registers.
BTW, probably a simpler initialization is something like this (my Java nay contain syntax errors):
Overall the Java synchronized stuff is way too uglier than it seems at first :-( And singletons are Evil.