background: We tend to ignore the multi-threaded situation when we implement the Singleton mode, that is, the code written in a single-threaded case is no problem, but when a multiple threads encountered, because the code is not written well, will cause a lot of problems, and these problems are very covert and difficult to troubleshoot.
Example 1: uniqueinstance with no volatile modifiers
Public classSingleton {Private StaticSingleton uniqueinstance; PrivateSingleton () {} Public StaticSingleton getinstance () {if(Uniqueinstance = =NULL) {//#1synchronized(Singleton.class) {//#2if(Uniqueinstance = =NULL) {//#3 uniqueinstance=NewSingleton ();//#4 System.out.println (Thread.CurrentThread (). GetName ()+ ": Uniqueinstance is initalized ..."); #5.1}Else{System.out.println (Thread.CurrentThread (). GetName ()+ ": Uniqueinstance is no null now ..."); #5.2} }}returnuniqueinstance; }}
1 Public classTestsingleton {2 Public Static voidMainFinalString[] args)throwsinterruptedexception {3 for(inti = 1; I <= 100000; i++) {4 FinalThread T1 =NewThread (NewThreadsingleton ());5T1.setname ("Thread" +i);6 T1.start ();7 }8 }9 Ten Public Static classThreadsingletonImplementsRunnable { One @Override A Public voidrun () { - singleton.getinstance (); - } the } -}
The result may be: (not really reproduced, too difficult to simulate)
1 thread2:uniqueinstance is initalized ... 2 thread3:uniqueinstance is initalized ...
Singleton was instantiated two times, inconsistent with our singleton pattern design expectations: Classes are always instantiated only once.
Cause Analysis:
1. Thread2 Enter # #, when the uniqueinstance of the child thread are empty, thread2 give up the CPU resources to THREAD3
Thread3 into the # #, when the child thread uniqueinstance are empty, thread3 give the CPO resources to Thread2
3. The thread2 will execute in turn, #3, #4, #5.1, and finally instantiate Uniqueinstancein Thread2. Thread2 execution finished yielding CPO resources to THREAD3
4. Thread3 then ran down to the # # time, because the uniqueinstance is still empty ( and did not get the latest from Thread2 in time ), so Thread3 will still execute # #, #5.1
5. Finally, the thread2 and thread3 are instantiated uniqueinstance
Example 2: uniqueinstance with volatile modifiers
There is no duplicate code here, because just add one more volatile to modify the member variable:uniqueinstance,
But the result is correct, one of the possible results:
Thread2:uniqueinstance is initalizedthread3:uniqueinstance isn't null now ...
Cause Analysis:
Volatile (JAVA5): Can ensure the visibility of multi-threading;
Read volatile: Every time a child thread uses a volatile variable for a statement, it copies a copy from the main thread, ensuring that the child threads are consistent with the main thread.
Write volatile: Whenever a child thread a statement to write a volatile variable, will be read after the synchronization to the main thread, so that the main thread to ensure that the variables are updated in time.
1. Thread2 enters # #, when the child threadUniqueinstance are empty (the Java memory model copies a copy from the main threaduniqueinstance=null to child thread Thread2), thread2 yields CPU resources to THREAD3
2.thread3 enters # #, when the child thread uniqueinstance is empty (the Java memory model copies a copy of the , thread3 yields CPO resources to thread2
3. Thread2 will execute # # #, #3, #4, # 5.1, finally instantiated in the thread2 of uniqueinstance ( Because it is a volatile modified variable, it is immediately synchronized to the main thread's variable to ). Thread2 execution finished yielding the CPO resource to Thread3
4. Thread3 then ran down to the # # time, and once again from the main thread copy a copy of the uniqueinstance ! =null back so Thread3 went straight to #5.2
5. Finally, the THREAD3 is no longer instantiated uniqueinstance
Reference article: How to use double-check locks in Java to implement a single case
Using volatile and synchronized to satisfy double check lock mechanism in singleton mode