英文名:Singleton Pattern。英文原話:Ensure a class has only one instance,and provide a global point of access to it。 單例模式的主要作用是確保一個類只有一個實例。 一、實現方式 1.靜態內部類 這是最好 ...
英文名:Singleton Pattern。英文原話:Ensure a class has only one instance,and provide a global point of access to it。
單例模式的主要作用是確保一個類只有一個實例。
一、實現方式
1.靜態內部類
這是最好的實現方式,廢話不多說,直接上代碼。
要點:私有的構造方法,保證外界無法直接實例化。一個內部靜態類來進行實例化,這樣能達到延遲初始化的效果。並且有 classloader 機制來保證初始化 instance 時只有一個線程。
public class Singleton { //構造方法私有,保證外界無法直接實例化 private Singleton(){} public static Singleton getInstance(){ return SingletonInstance.instance; } private static class SingletonInstance{ static Singleton instance = new Singleton(); } }
2.餓漢模式
public class HungrySingleton { private HungrySingleton() {} private static HungrySingleton instance = new HungrySingleton(); private static HungrySingleton getInstance() { return instance; } }
餓漢模式是在類載入的時候,就進行對象實例化。缺點就是如果這個類裡面有一個靜態方法,代碼里先使用了這個靜態方法,這時候就會完成實例化。這時候可能是還用不到這個實例的,就會造成浪費。就這一個問題,所以結合具體情況,餓漢模式使用也沒有大問題。
3.懶漢模式
public class LazySingleton { private static LazySingleton instance; private LazySingleton() { } public static LazySingleton getInstance() { if (instance == null) { instance = new LazySingleton(); } return instance; } }
懶漢模式是在第一次引用類時,才進行對象實例化。這種方式存在的問題是線程安全問題,就是多個線程同時去獲取實例的時候,可能會產生多個實例。
講到線程安全問題,通常就會想到synchronized加一個鎖來保證線程安全問題,沒錯,如下圖,可以這樣來保證線程安全
public class OneCheckLazySingleton { private static OneCheckLazySingleton instance; private OneCheckLazySingleton() { } public static synchronized OneCheckLazySingleton getInstance() { if (instance == null) { instance = new OneCheckLazySingleton(); } return instance; } }
但是這樣每次去獲取實例的時候,都會去走一遍這個鎖,其實在實例已經創建過後,就不會有線程安全問題了。所以有了改進版,雙重檢查鎖,如下
public class DoubleCheckLazySingleton { private volatile static DoubleCheckLazySingleton singleton; private DoubleCheckLazySingleton() { } public static DoubleCheckLazySingleton getSingleton() { if (singleton == null) { synchronized (Singleton.class) { if (singleton == null) { singleton = new DoubleCheckLazySingleton(); } } } return singleton; } }
這樣在實例已經創建後,再去獲取就不會走鎖了。註意這裡的volatile關鍵字,volatile禁止了JVM自動的指令重排序優化,是在jdk1.5及以後才有的。這樣,這種方式也是沒有問題的了,就是代碼相對複雜。
二、具體應用
先講一個概念,有狀態的類和無狀態的類。
有狀態的類:類裡面有成員變數,而且成員變數是會變的。
無狀態的類:類裡面沒有成員變數,或者有成員變數但是不可變的。
無狀態的類才適合使用單例模式,一些連接池,比如連接redis的jedis工具的JedisPool 可以使用單例。
再講一下分散式下的情況,分散式時,就是有多個jvm。一開始想到這個問題時,我還認為在分散式情況下會有問題,因為在一個jvm下有一個單例,整個系統不是只有一個實例了。 但仔細想了一下,因為是無狀態的類,對整個系統是沒有問題的。
好了,差不多就這些。謝謝閱讀~