什麼是單例模式?簡言之就是確保定義爲單例模式的類在程序中有且只有一個實例。單例模式的特色:java
只有一個實例 (只能有一個對象被建立)安全
自我實例化(類構造器私有)多線程
對外提供獲取實例的靜態方法性能
常見的單例模式實現方式有五種:測試
懶漢式(通常也稱之爲 飽漢式),具體代碼實現以下:線程
public class Singleton { /** * 自我實例化 */ private static Singleton singleton; /** * 構造方法私有 */ private Singleton() { System.out.println("建立單例實例..."); } /** * 對外提供獲取實例的靜態方法 */ public static Singleton getInstance() { if (null == singleton) { singleton = new Singleton(); } return singleton; } }
從代碼實現中能夠看到,實例並非在一開始就是初始化的,而是在調用 getInstance()方法後纔會產生單例,這種模式延遲初始化實例,但它並不是是線程安全的。code
public class SingleTonTest { /** * 多線程模式下測試懶漢模式是否線程安全 * * @param args */ public static void main(String[] args) { /** * 這裏我圖方便,直接用Executors建立線程池 * 阿里巴巴開發手冊是不推薦這麼作的 */ ExecutorService executorService = Executors.newFixedThreadPool(5); for (int i = 0; i < 20; i++) { executorService.execute(() -> System.out.println(Thread.currentThread().getName() + "::" + Singleton.getInstance())); } } }
測試結果截圖:對象
懶漢式是在運行時加載對象的,因此加載該單例類時會比較快,可是獲取對象會比較慢。且這樣作是線程不安全的,若是想要線程安全,能夠在getInstance()方法加上synchronized 關鍵詞修飾,但這樣會讓咱們付出慘重的效率代價。blog
提早建立好實例對象,調用效率高,但沒法延時加載,容易產生垃圾,線程安全。排序
public class Singleton { /** * 自我實例化 */ private static Singleton singleton = new Singleton(); /** * 構造方法私有 */ private Singleton() { System.out.println("建立單例實例..."); } /** * 對外提供獲取實例的靜態方法 */ public static Singleton getInstance() { return singleton; } }
public class Singleton { /** * 自我實例化,volatile修飾,保證線程間可見 */ private volatile static Singleton singleton; /** * 構造方法私有 */ private Singleton() { System.out.println("建立單例實例..."); } /** * 對外提供獲取實例的靜態方法 */ public static Singleton getInstance() { // 第一次檢查,避免沒必要要的實例 if (singleton == null) { // 第二次檢查,同步,避免產生多線程的問題 synchronized (Singleton.class) { if (singleton == null) { singleton = new Singleton(); } } } return singleton; } }
因爲singleton=new Singleton()
對象的建立在JVM中可能會進行重排序,在多線程訪問下存在風險,使用volatile
修飾signleton
實例變量,能禁止指令重排,使得對象在多線程間可見,可以有效解決該問題。
雙重檢查鎖定失敗的問題並不歸咎於 JVM 中的實現 bug,而是歸咎於 Java 平臺內存模型。內存模型容許所謂的「無序寫入」,這也是這些習語失敗的一個主要緣由
public class Singleton { /** * 構造方法私有 */ private Singleton() { System.out.println("建立單例實例..."); } private static class SingletonInner { private static Singleton instance = new Singleton(); } private static Singleton getInstance() { return SingletonInner.singleton; } }
這樣寫充分利用靜態內部類的特色——初始化操做和外部類是分開的,只有首次調用getInstance()方法時,虛擬機才加載內部類(SingletonInner.class)並初始化instance, 保證對象的惟一性。
public enum Singleton { INSTANCE }
感受異常簡單,默認枚舉類建立的對象都是單例的,且支持多線程。
文章首發於本人博客:www.developlee.top, 轉載請註明出處!
關注公衆號,後臺回覆666, 領取福利: