原文:https://www.cnblogs.com/damonhuang/p/5431866.htmlhtml
背景:咱們在實現單例模式的時候每每會忽略掉多線程的狀況,就是寫的代碼在單線程的狀況下是沒問題的,可是一碰到多個線程的時候,因爲代碼沒寫好,就會引起不少問題,並且這些問題都是很隱蔽和很難排查的。java
例子1:沒有volatile修飾的uniqueInstance多線程
public class Singleton { private static Singleton uniqueInstance; private Singleton(){ } public static Singleton getInstance(){ if(uniqueInstance == null){ //#1 synchronized(Singleton.class){ //#2 if(uniqueInstance == null){ //#3 uniqueInstance = new Singleton(); //#4 System.out.println(Thread.currentThread().getName() + ": uniqueInstance is initalized..."); //#5.1 } else { System.out.println(Thread.currentThread().getName() + ": uniqueInstance is not null now..."); //#5.2 } } } return uniqueInstance; } }
1 public class TestSingleton { 2 public static void main(final String[] args) throws InterruptedException { 3 for (int i = 1; i <= 100000; i++) { 4 final Thread t1 = new Thread(new ThreadSingleton()); 5 t1.setName("thread" + i); 6 t1.start(); 7 } 8 } 9 10 public static class ThreadSingleton implements Runnable { 11 @Override 12 public void run() { 13 Singleton.getInstance(); 14 } 15 } 16 }
這裏面的結果有可能會是:(沒有真正重現過,太難模擬了)ide
1 thread2: uniqueInstance is initalized... 2 thread3: uniqueInstance is initalized...
Singleton被實例化兩次了,和咱們的單例模式設計指望值不一致:類永遠只被實例化一次. 緣由分析: 1. thread2進入#1, 這時子線程的uniqueInstance都是爲空的,thread2讓出CPU資源給thread3 2. thread3進入#1, 這時子線程的uniqueInstance都是爲空的, thread3讓出CPO資源給thread2 3. thread2會依次執行#2,#3,#4, #5.1,最終在thread2裏面實例化了uniqueInstance。thread2執行完畢讓出CPO資源給thread3 4. thread3接着#1跑下去,跑到#3的時候,因爲#1裏面拿到的uniqueInstance仍是空(並無及時從thread2裏面拿到最新的),因此thread3仍然會執行#4,#5.1 5. 最後在thread2和thread3都實例化了uniqueInstance
例子2:用volatile修飾的uniqueInstance線程
這裏就不貼重複的代碼了,由於只是加多一個volatile來修飾成員變量:uniqueInstance,設計
可是結果倒是正確的了, 其中一個可能結果:htm
thread2: uniqueInstance is initalized thread3: uniqueInstance is not null now...
緣由分析:blog
volatile(java5):能夠保證多線程下的可見性;內存
讀volatile:每當子線程某一語句要用到volatile變量時,都會從主線程從新拷貝一份,這樣就保證子線程的會跟主線程的一致。資源
寫volatile: 每當子線程某一語句要寫volatile變量時,都會在讀完後同步到主線程去,這樣就保證主線程的變量及時更新。
1. thread2進入#1, 這時子線程的uniqueInstance都是爲空的(java內存模型會從主線程拷貝一份uniqueInstance=null到子線程thread2),thread2讓出CPU資源給thread3 2. thread3進入#1, 這時子線程的uniqueInstance都是爲空的(java內存模型會從主線程拷貝一份uniqueInstance=null到子線程thread2), thread3讓出CPO資源給thread2 3. thread2會依次執行#2,#3,#4, #5.1,最終在thread2裏面實例化了uniqueInstance(因爲是volatile修飾的變量,會立刻同步到主線程的變量去)。thread2執行完畢讓出CPO資源給thread3 4. thread3接着#1跑下去,跑到#3的時候,會又一次從主線程拷貝一份uniqueInstance!=null回來,因此thread3就直接跑到了#5.2 5. 最後在thread3再也不會重複實例化uniqueInstance了