Java設計模式-單例模式
轉自:http://blog.csdn.net/jason0539/article/details/23297037
Java中單例模式是一種常見的設計模式,單例模式的寫法有多種,這裏主要只介紹:懶漢式單例、餓漢式單例
單例模式確保某個類只有一個實例,而且自行實例化並向整個系統提供這個實例
選擇單例模式就是為了避免不一致狀態,同時也可減少資源消耗
一、懶漢式單例
1 //懶漢式單例類.在第一次調用的時候實例化自己 2 public class Singleton { 3 private Singleton() {} 4 private static Singleton single=null; 5 //靜態工廠方法 6 public static Singleton getInstance() { 7 if (single == null) { 8 single = new Singleton(); 9 } 10 return single; 11 } 12 }
Singleton通過將構造方法限定為private避免了類在外部被實例化,在同一個虛擬機範圍內,Singleton的唯一實例只能通過getInstance()方法訪問。
但是以上懶漢式單例的實現沒有考慮線程安全問題,它是線程不安全的,並發環境下很可能出現多個Singleton實例,要實現線程安全,有以下三種方式,都是對getInstance這個方法改造,保證了懶漢式單例的線程安全
1、在getInstance方法上加同步
1 public static synchronized Singleton getInstance() { 2 if (single == null) { 3 single = new Singleton(); 4 } 5 returnsingle; 6 }
2、雙重檢查鎖定
1 public static Singleton getInstance() { 2 if (singleton == null) { 3 synchronized (Singleton.class) { 4 if (singleton == null) { 5 singleton = new Singleton(); 6 } 7 } 8 } 9 return singleton; 10 }
3、靜態內部類
1 public class Singleton { 2 private static class LazyHolder { 3 private static final Singleton INSTANCE = new Singleton(); 4 } 5 private Singleton (){} 6 public static final Singleton getInstance() { 7 return LazyHolder.INSTANCE; 8 } 9 }
靜態內部類的方式比1、2種都要好一些,既實現了線程安全,有避免了同步帶來的性能影響
1 package com.singleton; 2 3 //懶漢式單例類,在第一次調用的時候實例化自己 4 //但是懶漢式單例的實現沒有考慮線程安全問題,它是線程不安全的,並發環境下很可能出現多個Singleton實例 5 //為什麽會出現線程安全問題呢? 6 //當多個線程去調用 Class.getInstance 時,在線程1自己的內存空間中,是沒有實例的,所以會新創建一個,線程2又去調用instance,又創建一個 7 8 //可以有三種改進方法:1.在getInstance方法上加同步 2.雙重檢查鎖定 3.靜態內部類(這種比較好) 9 public class SingletonLazy { 10 private SingletonLazy() { 11 } 12 13 // 在類被初始化的時候,不會為這個成員變量賦值(懶),必須要等到調用 getInstance 才會去創建實例 14 /*private static SingletonLazy singleton = null; (這裏不能加final,初始為null,加了final 之後,指向不能改變,就一直是null了) 15 16 // 靜態工廠方法 17 public static SingletonLazy getInstance() { 18 if (singleton == null) { 19 singleton = new SingletonLazy(); 20 } 21 return singleton; 22 }*/ 23 24 //靜態內部類的方式,在SingletonLazy類被初始化的時候,沒有成員變量可以去初始化 25 //當調用 getInstance 的時候,通過類加載器的方式 加載LazyHolder,同時加載LazyHolder 的單例實例 26 //因為類加載器的方式 是線程安全的,所以 這個也是線程安全的 27 private static class LazyHolder { 28 private static final SingletonLazy INSTANCE = new SingletonLazy(); //(加上final,創建後不會被修改) 29 } 30 31 public static final SingletonLazy getInstance() { 32 return LazyHolder.INSTANCE; 33 } 34 } 35
二、餓漢式單例
1 //餓漢式單例,在類初始化的時候,已經自行實例化 2 //在真正調用這個類的時候,才會完成這個類的初始化,為靜態成員變量賦初始值, 3 //保證getInstance 的時候,單例已經存在了 4 //為什麽這樣是線程安全的呢? 5 //猜想:類的加載初始化只會加載一次,SingletonHungry也只會被創建一次,(加上final,創建後不會被修改) 當多個線程去調用Class.getInstance方法時,getInstance做的不是創建,而是把我早就創建好的直接給你,所以拿到的是同一個實例(一個引用變量) 6 public class SingletonHungry { 7 private static final SingletonHungry singleton = new SingletonHungry(); 8 9 private SingletonHungry(){}; 10 11 public static SingletonHungry getInstance() { 12 return singleton; 13 } 14 }
餓漢式在類創建的同時就已經創建好一個靜態的對象供系統使用,以後不再改變,所以天生是線程安全的。
三、餓漢式和懶漢式的區別:
餓漢就是類一旦加載,就把單例初始化完成,保證getInstance的時候,單例是已經存在的了,
而懶漢比較懶,只有當調用getInstance的時候,才會去初始化這個單例
另外從以下兩點再區分以下這兩種方式:
1、線程安全:
餓漢式天生就是線程安全的,可以直接用於多線程而不會出現問題,
懶漢式本身是非線程安全的,為了實現線程安全有幾種寫法,分別是上面的1、2、3,這三種實現在資源加載和性能方面有些區別。
2、資源加載和性能:
餓漢式在類創建的同時就實例化一個靜態對象出來,不管之後會不會使用這個單例,都會占據一定的內存,但是相應的,在第一次調用時速度也會更快,因為其資源已經初始化完成,
而懶漢式顧名思義,會延遲加載,在第一次使用該單例的時候才會實例化對象出來,第一次調用時要做初始化,如果要做的工作比較多,性能上會有些延遲,之後就和餓漢式一樣了。
至於1、2、3這三種實現又有些區別,
第1種,在方法調用上加了同步,雖然線程安全了,但是每次都要同步,會影響性能,畢竟99%的情況下是不需要同步的,
第2種,在getInstance中做了兩次null檢查,確保了只有第一次調用單例的時候才會做同步,這樣也是線程安全的,同時避免了每次都同步的性能損耗
第3種,利用了classloader的機制來保證初始化instance時只有一個線程,所以也是線程安全的,同時沒有性能損耗,所以一般我傾向於使用這一種。
Java設計模式-單例模式