單例模式:采用一定的方法,使得軟件運行中,對于某個類只能存在一個實例對象,并且該類只能提供一個取得實例的方法。 分類:
實現思路:
一、餓漢式根據對象創(chuàng)建的時機,可以分為餓漢式和懶漢式。餓漢式,即在類加載的時候立即創(chuàng)建實例,根據所學知識我們可以很快想到要使用 特點
1.靜態(tài)常量方式class Singleton01{ //構造器私有,防止外部new private Singleton01(){ } //內部創(chuàng)建對象 private final static Singleton01 instance = new Singleton01(); //提供給外部創(chuàng)建實例的靜態(tài)方法 public static Singleton01 getInstance(){ return instance; } } 2.靜態(tài)代碼塊方式class Singleton02{ //構造器私有,防止外部new private Singleton02(){ } //內部創(chuàng)建對象 private static Singleton02 instance; static { instance = new Singleton02(); } //提供給外部創(chuàng)建實例的靜態(tài)方法 public static Singleton02 getInstance(){ return instance; } } 這種方式和靜態(tài)常量的實現方式邏輯和優(yōu)缺點是一樣的,只是寫法不同。 二、懶漢式懶漢式,即在類加載時并不實例化對象,等到使用對象實例的時候才去實例化,也被稱為懶加載效果。這樣做的好處可以節(jié)約資源,減少浪費,只有需要的時候采取創(chuàng)建,不需要就不會實例化。 1.普通方式(線程不安全)class Singleton01{ // 構造器私有 private Singleton01(){ } // 定義靜態(tài)變量,實例化留在獲取實例的getInstance方法,起到懶加載效果 private static Singleton01 instance; public static Singleton01 getInstance(){ // 判斷如果為空才創(chuàng)建,起到懶加載 if (instance == null){ instance = new Singleton01(); } return instance; } } 問題:在多線程情況下,假設類還未第一次實例化,此時兩個進程同時執(zhí)行到了 2.同步方法方式(線程安全)既然存在線程安全問題,肯定會想到使用 class Singleton02{ private static Singleton02 instance; private Singleton02(){ } //使用synchronized關鍵字來實現線程安全 public static synchronized Singleton02 getInstance(){ if (instance == null){ instance = new Singleton02(); } return instance; } } 這樣的方式可以起到線程安全的效果,但是每個線程都需要等待鎖,所以又會存在效率低的問題,于是有人想到了將鎖的范圍縮小到方法的內部,使用同步代碼塊的方式 3.同步代碼塊方式(線程不安全)這樣的方式好不好呢?先看代碼 class Singleton03{ private static Singleton03 instance; private Singleton03(){ } public static Singleton03 getInstance(){ if (instance == null){ // 這里的synchronized其實沒有實際意義,可能會產生多個實例 synchronized (Singleton03.class){ instance = new Singleton03(); } } return instance; } } 這樣鎖的范圍是變小了,但是還會存在多個線程同時判斷到 三、其他方式1.雙重檢查為了能夠實現懶加載的效果,同時兼顧效率,于是出現了這種寫法 class Singleton01{ //volatile,當有發(fā)生變化時即時儲存到內存中。防止指令重排 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; } } 使用雙重檢查,第一次檢查提升效率,第二次檢查保證線程安全,簡直美滋滋 2.靜態(tài)內部類利用靜態(tài)內部類在被調用時才會加載,即存在懶加載效果,所以也可以這樣寫 class Singleton02{ private Singleton02(){ } /* 靜態(tài)內部類在外部類裝載的時候不會馬上執(zhí)行,起到懶加載作用。 類的靜態(tài)屬性只有在第一次使用的時候才會加載,JVM在類加載時是線程安全的 */ private static class SingletonInstance{ private static final Singleton02 INSTANCE = new Singleton02(); } public static Singleton02 getInstance(){ return SingletonInstance.INSTANCE; } } 3.枚舉枚舉方式是最簡單的寫法,也是被很多人推崇的寫法 enum Singleton03{ INSTANCE; } 簡單明了... 四、小結使用單例模式,可以使一個類只存在一個實例對象,從而節(jié)省了系統(tǒng)資源。 上文中列出了8個寫法,其中懶加載的寫法存在線程安全和效率的問題,需要謹慎使用。比較推薦的寫法有5種:懶加載2種+其他方式3種。當認定單例的對象在軟件中一定會用到,可以使用懶加載,反之可以使用其他方式 |
|