The volatile
prevents memory writes from being re-ordered, making it impossible for other threads to read uninitialized fields of your singleton through the singleton's pointer.
Consider this situation: thread A discovers that uniqueInstance == null
, locks, confirms that it's still null
, and calls singleton's constructor. The constructor makes a write into member XYZ
inside Singleton, and returns. Thread A now writes the reference to the newly created singleton into uniqueInstance
, and gets ready to release its lock.
Just as thread A gets ready to release its lock, thread B comes along, and discovers that uniqueInstance
is not null
. Thread B
accesses uniqueInstance.XYZ
thinking that it has been initialized, but because the CPU has reordered writes, the data that thread A has written into XYZ
has not been made visible to thread B. Therefore, thread B sees an incorrect value inside XYZ
, which is wrong.
When you mark uniqueInstance
volatile, a memory barrier is inserted. All writes initiated prior to that of uniqueInstance
will be completed before the uniqueInstance
is modified, preventing the reordering situation described above.
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…