單例模式:採用必定的方法,使得軟件運行中,對於某個類只能存在一個實例對象,而且該類只能提供一個取得實例的方法。java
分類:安全
實現思路:多線程
想要實現單例,即不能讓外部隨意的去實例化對象。因此須要構造器私有線程
既然不容許外部去建立了,因此須要在類的內部建立對象code
外部須要使用對象,因此須要對外提供一個獲取實例的方法對象
根據對象建立的時機,能夠分爲餓漢式和懶漢式。餓漢式,即在類加載的時候當即建立實例,根據所學知識咱們能夠很快想到要使用static
關鍵字將屬性和類相關聯。如下提供兩種書寫方式供參考。進程
特色內存
class Singleton01{ //構造器私有,防止外部new private Singleton01(){ } //內部建立對象 private final static Singleton01 instance = new Singleton01(); //提供給外部建立實例的靜態方法 public static Singleton01 getInstance(){ return instance; } }
class Singleton02{ //構造器私有,防止外部new private Singleton02(){ } //內部建立對象 private static Singleton02 instance; static { instance = new Singleton02(); } //提供給外部建立實例的靜態方法 public static Singleton02 getInstance(){ return instance; } }
這種方式和靜態常量的實現方式邏輯和優缺點是同樣的,只是寫法不一樣。資源
懶漢式,即在類加載時並不實例化對象,等到使用對象實例的時候纔去實例化,也被稱爲懶加載效果。這樣作的好處能夠節約資源,減小浪費,只有須要的時候採起建立,不須要就不會實例化。get
class Singleton01{ // 構造器私有 private Singleton01(){ } // 定義靜態變量,實例化留在獲取實例的getInstance方法,起到懶加載效果 private static Singleton01 instance; public static Singleton01 getInstance(){ // 判斷若是爲空才建立,起到懶加載 if (instance == null){ instance = new Singleton01(); } return instance; } }
問題:在多線程狀況下,假設類還未第一次實例化,此時兩個進程同時執行到了if(instance==null)
,而將來得及往下執行時,此時二者校驗都成立,都會執行實例化操做,將有可能出現建立多個實例的問題。
既然存在線程安全問題,確定會想到使用synchronized
關鍵字來解決
class Singleton02{ private static Singleton02 instance; private Singleton02(){ } //使用synchronized關鍵字來實現線程安全 public static synchronized Singleton02 getInstance(){ if (instance == null){ instance = new Singleton02(); } return instance; } }
這樣的方式能夠起到線程安全的效果,可是每一個線程都須要等待鎖,因此又會存在效率低的問題,因而有人想到了將鎖的範圍縮小到方法的內部,使用同步代碼塊的方式
這樣的方式好很差呢?先看代碼
class Singleton03{ private static Singleton03 instance; private Singleton03(){ } public static Singleton03 getInstance(){ if (instance == null){ // 這裏的synchronized其實沒有實際意義,可能會產生多個實例 synchronized (Singleton03.class){ instance = new Singleton03(); } } return instance; } }
這樣鎖的範圍是變小了,可是還會存在多個線程同時判斷到if (instance == null)
,即便在後面加上鎖,依舊會在後續建立實例,只是延遲了一點而已,因此這種寫法不可取
爲了可以實現懶加載的效果,同時兼顧效率,因而出現了這種寫法
class Singleton01{ //volatile,當有發生變化時即時儲存到內存中。防止指令重排 private static volatile Singleton01 instance; private Singleton01(){ } //雙重檢查,解決線程同步問題,又保證效率 public static Singleton01 getInstance(){ if (instance == null){ // 第一次檢查,下降產生鎖的機率 synchronized (Singleton01.class){ if(instance == null){ // 第二次檢查,保證線程安全 instance = new Singleton01(); } } } return instance; } }
使用雙重檢查,第一次檢查提高效率,第二次檢查保證線程安全,簡直美滋滋
利用靜態內部類在被調用時纔會加載,即存在懶加載效果,因此也能夠這樣寫
class Singleton02{ private Singleton02(){ } /* 靜態內部類在外部類裝載的時候不會立刻執行,起到懶加載做用。 類的靜態屬性只有在第一次使用的時候纔會加載,JVM在類加載時是線程安全的 */ private static class SingletonInstance{ private static final Singleton02 INSTANCE = new Singleton02(); } public static Singleton02 getInstance(){ return SingletonInstance.INSTANCE; } }
枚舉方式是最簡單的寫法,也是被不少人推崇的寫法
enum Singleton03{ INSTANCE; }
簡單明瞭...
使用單例模式,可使一個類只存在一個實例對象,從而節省了系統資源。
上文中列出了8個寫法,其中懶加載的寫法存在線程安全和效率的問題,須要謹慎使用。比較推薦的寫法有5種:懶加載2種+其餘方式3種。當認定單例的對象在軟件中必定會用到,可使用懶加載,反之可使用其餘方式