單例模式
所謂的單例模式就是採取一定的方法保證整個的軟件系統中,對某個了類只能存在一個對象實例, 並且該類只提供一個取得其對象的實例方法(靜態方法). 比如Hibernate的SessionFactory,它充當數據存儲源的代理,並且負責創建Session對象. SessionFactory並不是輕量級的,一般情況下,一個項目通常只需要一個SessionFactory就夠,這是就會使用到單例模式
單例設計模式有八種方式: 五類
1)餓漢式(靜態常量) 2)餓漢式(靜態代碼塊)
3)懶漢式(線程不安全) 4)懶漢式(線程安全,同步方法) 5)懶漢式(線程安全,同步代碼塊)
6)雙重檢查 7)靜態內部類 8)枚舉
1)餓漢式(靜態常量) 應用實例
步驟如下:
1.構造器私有化 (防止通過new來創建實例)
2.類的內部創建對象
3.向外暴露一個靜態的公共方法 getInstance
4.代碼實現
public class SingletonTest01 { public static void main(String[] args) { Singleton instance = Singleton.getInstance(); Singleton instance1 = Singleton.getInstance(); System.out.println(instance == instance1); //true } } //餓漢式(靜態變量) class Singleton { // 1.使用構造器私有化 private Singleton() { } // 2.本類內部創建對象實例 類加載時創建對象實例 private final static Singleton instance = new Singleton(); // 3.提供一個公有的靜態方法,返回實例對象 public static Singleton getInstance() { return instance; } }
優缺點說明:
1)優點:這種方法簡單,就是在類裝載的時候就完成實例化.避免了線程的同步問題.
2)缺點:在類加載的時候就完成了實例化,沒有達到Lazy Loading的效果.如果從始至終沒有使用過這個實例,就會造成內存浪費
3)這種方式基於classLoader機制避免了多線程同步的問題,不過,instance在類裝載時就實例化,在單例模式中大多數都是調用getInstance方法,但是導致類裝載的原因有很多種,因此不能確定有其他的方式(或者其他的靜態方法)導致類裝載,這時候初始化instance就沒有達到Lazy loading的效果
4)結論:這種模式可用,可能造成內存浪費
2)餓漢式(靜態代碼塊)
/** * 將靜態代碼塊中創建單例對象 * * 優缺點說明: * 1)這種方和上面的方式其實類似,只不過將類的實例化過程放在了靜態代碼塊中, * 也是類裝載的時候,就執行靜態代碼塊中 的代碼,初始化類的實例,優越點和上面是一樣的 * 2)結論:這種單例模式可用,但是會造成內存浪費 * */ .... private static Singleton instance; static { //在靜態代碼塊中創建單例對象 instance=new Singleton(); } ......
3)懶漢式(線程不安全的)
public class SingletonDemo3 { public static void main(String[] args) { System.out.println("懶漢式1,線程不安全-----"); Singleton instance = Singleton.getInstance(); //爲空 直接返回 Singleton instance1 = Singleton.getInstance(); //2.不爲空 直接返回 System.out.println(instance == instance1); //true } } class Singleton { private static Singleton instance; private Singleton() { } //提供一個靜態的公有方法,使用到getInstance時纔會創建instance //即是懶漢式 public static Singleton getInstance() { //線程不安全的-----潛在的風險 if (instance == null) { instance = new Singleton(); } return instance; } }
優缺點聲明:
1)起到了懶加載的效果,但是隻能在單線程下使用
2)如果在多線程下,一個線程進入了if(Singleton==null)判斷語句塊,還未往下執行,另一個線程也通過這個判斷語句,這時會產生多個實例.所以在多線程環境下不可使用這種方式
3)結論:在實際開發中,不要使用這種方式.
4)懶漢式(線程安全,同步方法)
........... //提供一個靜態的公有方法,加入同步處理的代碼,解決線程安全問題 //加一個關鍵字 synchronized 線程安全 public static synchronized Singleton getInstance() { if (instance == null) { instance = new Singleton(); } return instance; } } .............
優缺點說明:
1)解決了線程不安全問題
2)效率太低,每個線程在想獲得類的實例的時候,執行getInstance()方法都要進行同步,而其實這個方法只執行一次代碼就夠了,後面的需要直接return就行了
3)結論:實際開發中,不推薦使用
5)懶漢式(線程安全,同步代碼塊)-----》線程安全都沒有解決
class Singleton{ private static Singleton instance; private Singleton{} public static Singleton getInstance(){ if(instance==null){ synchronized (Singleton.class){ instance=new Singleton(); } } return instance; } }
優缺點說明:
1).這種方式,本意是想對第四種實現方法的改進,因爲前面同步方法的效率太低,更改爲同步實例化的代碼塊
2).但是這種方法並不能起到線程同步的作用。和第三種實現方式遇到的 情形一致,假如一個線程進入了if(singleton==null)判斷語句塊,還未來來得及往下執行,另一個線程也通過這個判斷語句,這便會產生多個實例
3).在實際的開發中,不能使用這種方式。
6)雙重檢查---》延遲加載,效率較高
public class DoubleCheck { public static void main(String[] args) { System.out.println("雙重檢查"); Singleton instance = Singleton.getInstance(); Singleton instance2 = Singleton.getInstance(); System.out.println(instance==instance2); } } //雙重檢查 class Singleton{ private static volatile Singleton instance; private Singleton() { } //提供一個靜態公有方法,加入雙重檢查代碼,解決線程安全問題,同時解決了懶加載問題 //同時保證了效率,推薦使用 public static synchronized Singleton getInstance() { if (instance == null) { synchronized (Singleton.class){ if (instance==null){ instance=new Singleton(); } } } return instance; } }
優缺點說明:
1)Double-check 概念是多線程開發中常使用到的,如代碼中所示,我們判斷了兩次if(singleton==null)檢查,這樣就可以保證線程安全了
2)這樣,實例化代碼只執行一次,後面再次訪問時,判斷if(singleton==null),直接return實例化對象;
3)延遲加載,效率較高
4)結論:在實際開發中,推薦使用這種單例設計模式
7)枚舉
public class SingletonDemo{ public static void main(String[] args) { Singleton instance = Singleton.INSTANCE; Singleton instance1 = Singleton.INSTANCE; System.out.println(instance==instance1); instance.sayOk(); } } enum Singleton{ INSTANCE; //一個屬性 public void sayOk(){ System.out.println("ok(~o ̄3 ̄)~"); } }
優缺點說明:
1)這藉助jdk1.5中添加的枚舉來實現單例模式,不僅能避免多線程問題,而且還能防止反序列化來創建對象的問題
2)這種方式是 Effective java作者Josh Bloch提倡創建單例模式的方式
3)結論:推薦使用