1. Let’s call i-thread the thread that first enter the synchronized block. This thread will eventually change the value of bInited variable, even if initially only in its local memory 2. Though compiler has ability to transform getInstance() code, and then runtime has ability change the execution order of operations, no such optimization is allowed to break the minimum ITS for the i-thread executed in isolation 3. The result of the call hash = instance.hashCode() is unpredictable before the constructing of the instance object. NOTE 1: According to the java API, the default hashCode() is typically implemented by converting the internal address of the object into an integer. But if that is not good enough for unpredictability, we can consider implementing hashCode() for Singleton as Random rand = new Random(Date.getTime()); int hash = rand.nextInt(); and then let compiler or runtime try to predict inside or before the constructor the value of Date.getTime() placed in the original code order after the constructor! 4. The call bInited = oracle(hash) uses the results of the call instance.hashCode()and unpredictable, as we assumed 5. Therefore the compiler or runtime optimization can’t assign the value (true) of the bInited variable before the complete constructing the Singleton object and assigning to the instance variable in the local memory of the i-thread 6. After the i–thread exits the synchronized block and before any other thread enter that block, the values of the instance and bInited variables in the main memory will be set to the value from i-thread local memory. Both set operations are atomic. 7. Now consider the behavior of any other thread, let’s call it a w-thread, in minimum ITS semantics. That means, we can assume the order of executions for the w-thread as it specified in the Singleton code, but the shared variables can have “unexpected” values. That is what we will look at now. Let’s use the following notations: M(bInited) is the copy of the bInited variable in the main memory

is the copy of the variable in the main memory I(bInited) is the copy of the bInited variable in the local i-thread memory

is the copy of the variable in the local i-thread memory W(bInited) is the copy of the bInited variable in the local w-thread memory M(instance) is the copy of the instance variable in the main memory

is the copy of the variable in the main memory I(instance) is the copy of the instance variable in the local i-thread memory

is the copy of the variable in the local i-thread memory W(instance) is the copy of the instance variable in the local w-thread memory 8. Let’s consider in turn following interesting cases for w-thread. 8.a. The w-thread waits on the synchronized block for i-thread to leave the block – piece of cake. First i-thread leaves the synchronized block, then java synchronizes i-thread local variables to the main memory, I(bInited) => M(bInited) I(instance) => M(instance) then on w-thread entry to the synchronized block java synchronizes the w-thread local variables from the main memory, M(bInited) => W(bInited) M(instance) => W(instance) and now the w-thread has a correct view of shared variables, same view as the i-thread has. 8.b. The w-thread is inside or before the FIRST CHECK operation when i-thread leaves the synchronized block. Note, that W(bInited) and W(instance) are initialized from the main memory ether before or after the I(bInited)and I(instance) were synchronized to M(bInited)and M(instance), which happens after t-thread left the synchronized block. So each of W(bInited) and W(instance) can only have either initial value (as defined in the class Singleton) or the value produced by the i-thread, in any possible combination. Then w-thread first checks the value of the variable W(bInited). It value is false w-thread jumps to the synchronized block, essentially as in the case 7.a. If W(bInited)==true, then i-thread already has assigned I(bInited)=true and java already synchronized along the chain I(bInited) => M(bInited) => W(bInited) Therefore the i-thread already has initialized the Singleton object and at the very least the I(instance) is references that object. Now next the w-thread checks the value of the W(instance) variable. If W(instance)==null then java not yet synchronizes along the chain I(instance) => M(instance) => W(instance) but no worry, w-thread than only jumps on the entry of the synchronized block and discover the new value of the instance variable there, as in the 7.a case. But if W(instance)=!null, then the only value that the W(instance)can only have is the value of the I(instance). Therefore the w-thread will return the fully constructed instance of the Singleton. 9. So for any thread which obtains the instance variable through the getInstance() method it is guaranteed that the Singleton object has already been created and fully initialized by i-thread.