Looking more carefully at this, rather than trusting my first look...
Ok, i see that there is potential for a second thread to jump ahead of the first one which is busy performing the meat of the init. This second thread then might run into trouble if it tries to do anything with the instance before the first thread finishes the setup/initialization.
So, Christopher, you were right, there is an issue here, just not the original one. Sorry, and thanks for pressing me to keep thinking about it.
So, if the user is running things on pre-1.5, then JIT/VM can re-order the setting of init=true within the synchronized block. Then, if the VM decides to do that and another thread comes along and finds init=true, thus skipping the synchronization, it could pass up the original thread which is still busy performing the initialization. If it then gets on to where it tries to use the incompletely initialized (but fully instantiated instance, it could get into trouble.
So, while Lei's patch completely eliminates the chances of having the initialization code called multiple times, it does introduce the chance of even less-frequent (and thus much harder to debug) synchronization problems for those running on older JVMs. Apply Murphy's Law, and we can be sure it will happen. We'd better just synchronize the block/method, without trying to skip it via double checking. If anyone's interested, we could leave a note that once we require 1.5, we can use DCL with the volatile keyword happily.