再說單例模式的線程安全問題


今天和同事聊起了單例模式的線程安全,我說如果不做任何措施,單例模式在多線程下是不安全的,得到的“單例”實際上並不是單例。但是為什么不是單例呢?由此我上網查了一下,在使用單例模式時,一定要注意線程安全問題,之前的寫法沒有任何問題。如下:

 1 package day_5_singleton;
 2 
 3 /**
 4  * 單例
 5  * 
 6  * @author turbo
 7  *
 8  *         2016年9月8日
 9  */
10 public class Singleton {
11     private static Singleton instance;
12 
13     private Singleton() {
14     }
15 
16     public static synchronized Singleton GetInstance() {
17 
18         if (instance == null) {
19             instance = new Singleton();
20         }
21         
22         return instance;
23     }
24 }

問題就在於,synchronized對整個方法加鎖,形成同步機制,這樣雖然解決了單例模式的線程安全問題,但是卻產生另外一個問題性能問題,對方法加鎖這個顆粒度有點大,我們稍微改進一下。如下:

 1 package day_5_singleton;
 2 
 3 /**
 4  * 單例
 5  * 
 6  * @author turbo
 7  *
 8  *         2016年9月12日
 9  */
10 public class Singleton {
11     private static Singleton instance;
12 
13     private Singleton() {
14     }
15 
16     public static Singleton GetInstance() {
17 
18         if (instance == null) {
19             synchronized (Singleton.class) {
20                 if (instance == null){
21                     instance = new Singleton();
22                 }
23             }
24         }
25         
26         return instance;
27     }
28 }

利用雙重鎖的方式這樣顆粒度變小了,但還是利用同步的方式來解決資源共享問題。其實這上面兩種寫法稱之為“懶加載”,即在用到的時候再來實例化。

我們再次修改代碼,如下。

 1 package day_5_singleton;
 2 
 3 /**
 4  * 單例
 5  * 
 6  * @author turbo
 7  *
 8  *         2016年9月12日
 9  */
10 public class Singleton {
11     private static Singleton instance = new Singleton();
12 
13     private Singleton() {
14     }
15 
16     public static Singleton GetInstance() {
17         return instance;
18     }
19 }

我們不利用線程同步的方式,而是在類被加載的時候就生成一個實例對象。這稱之為“勤加載”,這個帶來的問題就是,不管這個單例有沒有用到都會一直存在。

兩者都有其優缺點,但相對於利用線程同步的方式來解決線程安全問題,“勤加載”會是一個較為明智的選擇。

2016.9.16補充:之所以懶加載不采取任何措施造成的線程不安全問題,是因為在程序中出現了“競態條件(Race Condition)”,由於不恰當的執行時序而出現不正確的結果。最常見的競態條件類型就是“先檢查后執行(Check-Then-Act)”操作,即通過一個可能失效的觀測結果來決定下一步的動作。——《Java 並發編程實戰》


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM